Codeforces Round #782 (Div. 2)

CF1659A Red Versus Blue

B 去均分 R,咋写都能过。

CF1659B Bit Flipping

可以看作,把每个元素都作了 \(k\) 遍操作。

然后我们可以看作再做 \(k\) 遍操作,每次操作可以翻转一位。

把全部变成 \(1\),剩下的操作全部累在最后一位,使它的负面影响最小。

CF1659C Line Empire

王国只能一次占领(但我感觉没有这个条件也只能这么做)。

然后发现,一旦我占领了一个城市后我没有迁都,那么我再也不会迁都。简单计算推导可以证明这一点。

然后枚举从哪个位置开始迁都就可以了。

CF1659D Reverse Sort Sum

constructive 的题目确实很有意思。

宏观来看,发现对于每个位置先在一段时间里一直是 1 ,然后就一直都变成 0 了。

不妨考虑一些简单的情况。对于第一个位置,注意到一旦出现一个 0,他就再也不会是 1,则 \(c_1\) 恰好是第一个出现 0 的位置。

简单讨论发现 \(c_i\) 恰好就是第 \(i\)0 的位置(如果第 \(i\) 个位置是 1)。

则,我们可以轻易一次确定每个位置的值。

int c[MAXN] , a[MAXN];
 
int main() {
	int T;
	read(T);
	while(T -- > 0) {
		int n;
		read(n);
		for (int i = 1; i <= n; ++i) read(c[i]) , a[i] = 1;
		for (int i = 1; i <= n; ++i) {
			if(a[i] && c[i]) {
				a[c[i] + 1] = 0;
				if(c[i] == n) break;
			}
			else {
				a[c[i] + i] = 0;
				if(c[i] + i > n) break;
			}
		}
		for (int i = 1; i <= n; ++i) write(a[i] , ' ');
		puts("");
	}
	return 0;
}

CF1659E AND-MEX Walk

constructive 的确实比较有难度。

求最小路径权值。优先判断能否找到权值为 0 的路径。因为是 mex 值,就是说我们能够找到一条至少同时包含一位的路径。

然后判断能否为 1。假设不能为 1,即无论如何路径中都会出现 1,因此一定不会出现 2 ,否则必不可能出现 1

因此现在问题只剩下如何判断能否不造出 1。那么就要求经过一条偶数边权,同时过程中必须要有其他位伴随 1 存在。

预处理一下,从偶数边权的两端开始dp,计算每个点为起点能否得到这样的路径就好了。

const int MAXN = 1e5 + 5;
 
int f[35][MAXN] , n , m , chk[MAXN] , dp[MAXN][35];
int findSet(int k , int x) {
	if(f[k][x] != x) f[k][x] = findSet(k , f[k][x]);
	return f[k][x];
} 
 
vector <pii> G[MAXN];
 
struct Edge {
	int u , v , w;
}E[MAXN];
 
void Union(int k , int u , int v) {
	u = findSet(k , u) , v = findSet(k , v);
	f[k][u] = f[k][v];
	return;
}
 
int main() {
	read(n),read(m);
	for (int i = 1; i <= m; ++i) {
		read(E[i].u),read(E[i].v),read(E[i].w);
		G[E[i].u].push_back(mp(E[i].v , E[i].w));
		G[E[i].v].push_back(mp(E[i].u , E[i].w));
		if(!(E[i].w & 1)) {
			chk[E[i].u] = chk[E[i].v] = 1;
			for (int j = 1; j < 30; ++j) dp[E[i].u][j] = dp[E[i].v][j] = 1;
		} 
	}
	
	queue <pii> q;
	for (int i = 1; i <= n; ++i) if(chk[i]) for (int j = 1; j < 30; ++j) q.push(mp(i , j));
	
	while(!q.empty()) {
		int x = q.front().fs , y = q.front().sc;
		q.pop();
		for (auto v:G[x]) {
			if((v.sc & 1) && (v.sc & (1 << y)) && !dp[v.fs][y]) {
				q.push(mp(v.fs , y));
				dp[v.fs][y] = 1;
				chk[v.fs] = 1;
			}
		}
	}
	
	for (int i = 0; i < 30; ++i) for (int j = 1; j <= n; ++j) f[i][j] = j;
	
	for (int i = 0; i < 30; ++i) {
		int v = (1 << i);
		for (int j = 1; j <= m; ++j) if(E[j].w & v) {
			Union(i , E[j].u , E[j].v);
		}
	}
	
	
	int Q;
	read(Q);
	while(Q -- > 0) {
		int u , v;
		read(u),read(v);
		int fl = 0;
		for (int i = 0; i < 30; ++i) {
			if(findSet(i , u) == findSet(i , v)) {
				fl = 1;
				puts("0");
				break;
			}
		}
		if(fl) continue;
		if(chk[u]) puts("1");
		else puts("2");
	}
	
	return 0;
} 

CF1659F Tree and Permutation Game

在一棵树上,Alice 始终可以把我们的不在位的点变成两个,因为 Bob 最多只能影响两个位置。

然后我可以很轻易地让他们靠在一起。然后,我可以让一个飞的很远,然后换到正确位置,然后棋子必须移动,但影响不到很远的位置,因此再换一次就好了。

发现很远的要求就是直径达到 3。因此直径大于等于 3 的树 Alice 必胜。

剩下一个菊花图的情况。

分类讨论一下:

  1. 如果根节点不在位,并且棋子在根节点上,那 Bob 赢了。因为 Bob 只要保证根节点永远无法归位就好了。

  2. 如果根节点不在位,棋子在叶子上,那根节点必须一步到位。

  3. 如果根节点在位,棋子在根节点,发现至少交换次数为奇数就好了,因为任意一个棋子在根节点时,我们都剩奇数次,而到最后只会有两个数未归位,所以棋子在根节点时就可以直接一步归位。

  4. 如果根节点在位,棋子在叶子节点,下一步棋子必往根节点走,划归为 3情况即可。

vector <int> G[MAXN];
int n , p[MAXN] , deg[MAXN] , x , f[MAXN] , a[MAXN];
 
int findSet(int x) {
	if(f[x] != x) return f[x] = findSet(f[x]);
	return f[x];
}
 
int main() {
	int T;read(T);
	while(T -- > 0) {
		read(n),read(x);
		for (int i = 1; i <= n; ++i) deg[i] = 0;
		pii mx = mp(0 , 0);
		for (int i = 1; i < n; ++i) {
			int u , v;
			read(u),read(v);
			deg[u] ++ , deg[v] ++;
		}
		int fl = 1;
		for (int i = 1; i <= n; ++i) read(a[i]) , p[a[i]] = i , mx = max(mx , mp(deg[i] , i)) , fl &= (p[i] == i);
		int rt = mx.sc;
		if(deg[rt] != n - 1 || fl) {
			puts("Alice");
			continue;
		}
		if(p[rt] != rt && (x == rt || p[x] == rt)) {
			puts("Bob");
			continue;
		} 
		else if(p[rt] != rt) {
			for (int i = 1; i <= n; ++i) if(p[i] == rt) {
				swap(p[i] , p[rt]);
				break;
			}
			x = rt;
		}
		fl = 1;
		for (int i = 1; i <= n; ++i) f[i] = i , fl &= (p[i] == i);
		if(fl) {
			puts("Alice");
			continue;
		}
		for (int i = 1; i <= n; ++i) f[findSet(i)] = f[findSet(p[i])];
		int num = n;
		for (int i = 1; i <= n; ++i) if(findSet(i) == i) num --;
		if(num == 1 && x != rt && p[x] == x) {
			puts("Alice");
			continue;
		}
		num += (x != rt);
		if(num & 1) puts("Alice");
		else puts("Bob");
	}
	return 0;
} 
posted @ 2022-05-20 20:42  Reanap  阅读(88)  评论(0编辑  收藏  举报