ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Codeforces Round #798 Div.2 C-E 题解

2022-07-03 01:04:33  阅读:160  来源: 互联网

标签:node int 题解 son vis maxn Div.2 798 dp


Problem C

传送门

Solution

看完题面不难想到一个贪心思路:

从根节点开始 DFS,每次截断节点数更多的子树,然后递归到另一棵子树继续向下计算。

看起来十分美好,但有一个问题:如果两棵子树大小一样,结构又不清楚,怎么办?

这个时候就珂以用动态规划来解决了。

设 \(dp(u)\) 表示 \(u\) 被感染后其子树内能被救下的最大节点数。

则 \(dp(u) = \max(dp(son_1) + size(son_2)-1,dp(son_2)+size(son_1)-1)\)。

其中 \(son_1,son_2\) 为 \(u\) 的子节点,\(size(v)\) 表示以 \(v\) 为根的子树的大小。

那么我们跑一遍树形 DP 即可qwq

时间复杂度 \(O(N)\)。

Code

#include <bits/stdc++.h>
#define pb emplace_back
using namespace std;
const int maxn = 5e5 + 5;
vector<int> g[maxn];
vector<int> son;
int sz[maxn],n,dp[maxn];
int ans;
void dfs(int u,int fa) {
	sz[u] = 1;
	for(auto v : g[u]) {
		if(v == fa)continue ;
		dfs(v , u);
		sz[u] += sz[v];
	}
	for(auto v : g[u]) {
		if(v != fa)son.pb(v);
	}
	if(son.size() == 0)dp[u] = 0;
	else if(son.size() == 1)dp[u] = sz[son[0]] - 1;
	else dp[u] = max(dp[son[0]] + sz[son[1]] - 1 , dp[son[1]] + sz[son[0]] - 1);
	son.clear();
	return ;
}
void work() {
	ans = 0;
	scanf("%d",&n);
	for(int i = 1;i <= n;++ i)sz[i] = 0,g[i].clear();
	for(int i = 1;i < n;++ i) {
		int x,y;
		scanf("%d%d",&x,&y);
		g[x].pb(y);
		g[y].pb(x);
	}
	dfs(1 , 0);
	printf("%d\n",dp[1]);
	return ;
}
int main() {
	int T;
	scanf("%d",&T);
	while(T --)work();
	return 0;
}

Problem D

传送门

参考题解

Solution

不难想到暴力思路:枚举每个点,求出黑点与该点的曼哈顿距离最大值并更新答案。

这样最差会达到 \(O(N^2M^2)\),考虑优化。

显然不是所有的黑点都能对答案产生贡献,经观察,只有最左上,左下,右上,右下的黑点能产生最大值。

那么我们找出这四个点就珂以了。

对于点 \((x,y)\),如果它在左上,则 \(x+y\) 取值最小,右下则为 \(x+y\) 最大。

同理可得,如果在左下则 \(x-y\) 值最小,右上则 \(x-y\) 值最大。

顺着这个思路,找到那四个点,再进行暴力枚举即可。

时间复杂度 \(O(NM)\)。

Code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3 + 5;
struct node {
	int x,y;
	node() {
		x = y = 0;
	}
	node(int x,int y):x(x),y(y){}
}a[5];
bool vis[5];
int n,m;
void work() {
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= 4;++ i)a[i] = node(0 , 0),vis[i] = false;
	for(int i = 1;i <= n;++ i) {
		for(int j = 1;j <= m;++ j) {
			char c;
			scanf(" %c",&c);
			if(c != 'B')continue ;
			if(!vis[1]||i + j > a[1].x + a[1].y)a[1] = node(i , j),vis[1] = true;
			if(!vis[2]||i + j < a[2].x + a[2].y)a[2] = node(i , j),vis[2] = true;
			if(!vis[3]||i - j > a[3].x - a[3].y)a[3] = node(i , j),vis[3] = true;
			if(!vis[4]||i - j < a[4].x - a[4].y)a[4] = node(i , j),vis[4] = true;
		}
	}
	int x = 0,y = 0,tot = 0x3f3f3f3f;
	for(int i = 1;i <= n;++ i) {
		for(int j = 1;j <= m;++ j) {
			int cur = 0;
			for(int k = 1;k <= 4;++ k) {
				cur = max(cur , abs(a[k].x - i) + abs(a[k].y - j));
			}
			if(cur < tot) {
				tot = cur;
				x = i;
				y = j;
			}
		}
	}
	printf("%d %d\n",x,y);
	return ;
} 
int main() {
	int T;
	scanf("%d",&T);
	while(T --)work();
	return 0;
}

Problem E

传送门

参考题解

Solution

挺搞的一道结论题。证明是我看了滴叉以及几位大佬的题解以后弄出来的QAQ。

首先要注意,\(0\) 是始终无法连边的,所以所有的 \(0\) 开始时要先加上 \(1\)。

下面就是神奇的结论:在处理了 \(0\) 的基础上,答案至多会再 \(+2\)。

证明:

首先发现奇数全部有连边,零零散散的都是偶数。

设偶数中 \(\operatorname{lowbit}\) 的最大值出现的位置为 \(a_{p_1},a_{p_2}\ldots a_{p_k}\) 。

  • 如果 \(k=1\),那么我们只要令 \(a_{p_1} \gets a_{p_1} -1\),那么它也会变成奇数,同时它的二进制位包含了所有偶数的 \(\operatorname{lowbit}\) 值,所以它与偶数间也有连边,整个图联通。

  • 如果 \(k \gt 1\),那么随便找两个位置 \(i,j(i \neq j)\),令 \(a_{p_i} \gets a_{p_i}-1,a_{p_j}\gets a_{p_j}+1\)。那么所有 \(p_i,p_j\) 这两个位置都与奇数有了连边,且 \((p_i,p_j)\) 也连上了边,同时 \(\operatorname{lowbit}\) 值低于最大值的所有点都与 \(p_i\) 有连边,\(\operatorname{lowbit}\) 值等于最大值的所有点都与 \(p_j\) 连边,又因 \((p_i,p_j)\) 有连边,所以整个图都连通。

综上,我们至多需两次操作。证毕。

据此,直接针对除处理 \(0\) 以外的操作数为 \(0,1,2\) 三种情况进行计算即可。

时间复杂度 \(O(N^2\log N)\)。(计入了 dsu 的时间复杂度)

Code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e3 + 5;
int n,a[maxn],s;
int lowbit(int x) {
	return x & -x;
}
bool vis[maxn];
void dfs(int x) {
	if(vis[x])return ;
	s += vis[x] = true;
	for(int i = 1;i <= n;++ i) {
		if(i != x&&!vis[i]&&a[i] & a[x]) {
			dfs(i);
		}
	}
	return ;
}
int pre[maxn];
int find(int x) {
	return x == pre[x] ? x : pre[x] = find(pre[x]);
}
bool check() {
	for(int i = 1;i <= n;++ i)pre[i] = i;
	for(int k = 0;k < 30;++ k) {
		int fir = n;
		for(int i = 1;i < n;++ i) {
			if(a[i] >> k & 1) {
				fir = i;
				break ;
			}
		}
		for(int i = fir + 1;i <= n;++ i)
			if((a[i] >> k & 1)&&find(i) != find(fir))pre[find(i)] = find(fir);
	}
	for(int i = 2;i <= n;++ i) {
		if(find(i) != find(1))return false;
	}
	return true;
}
void work() {
	int maxl = 0,ans = 0;
	scanf("%d",&n);
	for(int i = 1;i <= n;++ i)scanf("%d",&a[i]),vis[i] = false;
	for(int i = 1;i <= n;++ i)ans += (!a[i]) ? ++ a[i] : 0,maxl = max(maxl , lowbit(a[i]));
	s = 0;
	dfs(1);
	if(s == n) {
		printf("%d\n",ans);
		for(int i = 1;i <= n;++ i) {
			printf("%d ",a[i]);
		}
		puts("");
		return ;
	} 
	for(int i = 1;i <= n;++ i) {
		-- a[i];
		if(check()) {
			printf("%d\n",ans + 1);
			for(int k = 1;k <= n;++ k) {
				printf("%d ",a[k]);
			}
			puts("");
			return ;
		}
		++ a[i];
		++ a[i];
		if(check()) {
			printf("%d\n",ans + 1);
			for(int k = 1;k <= n;++ k) {
				printf("%d ",a[k]);
			}
			puts("");
			return ;
		}
		-- a[i];
	}
	int cnt = 0;
	for(int i = 1;i <= n;++ i) {
		if(lowbit(a[i]) == maxl) {
			++ cnt;
			if(cnt == 1)-- a[i];
			else {
				++ a[i];
				break ;
			}
		}
	}
	printf("%d\n",ans + 2);
	for(int i = 1;i <= n;++ i)printf("%d ",a[i]);
	puts("");
	return ;
}
int main() {
	int T;
	scanf("%d",&T);
	while(T --) {
		work();
	}
	return 0;
}

完结撒花*★,°*:.☆( ̄▽ ̄)/$:*.°★*

标签:node,int,题解,son,vis,maxn,Div.2,798,dp
来源: https://www.cnblogs.com/663B/p/16439019.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有