Educational Codeforces Round 128 (Rated for Div. 2)

险些三题选手身败名裂。幸好 E 太水了。

CF1680A Minimums and Maximums

不会,略。

CF1680B Robots

不会,略。

CF1680C Binary String

0 不减少,答案就不会变小。假设现在有 \(x\)\(0\)

那么我删去了 \(y\)\(0\) ,那么我删去 \(x - y\)\(1\) 也不会让我的答案变大。

因此发现,我可以钦定我删去 \(x\) 个数,那么一定可以得到最优答案。

枚举开头删多少个就可以了。

CF1680D Dog Walking

注意到我们需要求到到达位置的最大值和最小值。

为了达到最大值,我们可以一直放 \(k\) ,为了到最小值一直放 \(-k\)

可不可行就看我后面的空白一直填 \(k\)\(-k\) 使得我的区间跨过 \(0\)

我们可以枚举我们的最大值在什么时候取到,枚举最小值在什么时候取到。在取到最大值之前我们一直用 \(k\) ,然后一直取 \(-k\)

检查能否成功结束。

int main() {
	read(n),read(k);
	for (int i = 1; i <= n; ++i) read(a[i]) , v[i] = v[i - 1] + a[i];
	for (int i = 1; i <= n; ++i) pre[i] += pre[i - 1] + (a[i] == 0);
	if(v[n] + pre[n] * k < 0 || v[n] - pre[n] * k > 0) {
		puts("-1");
		return 0;
	}
	LL ans = 0;
	for (int i = 0; i <= n; ++i) 
		for (int j = 0; j <= n; ++j) if(i != j) {
			if(i < j) {
				int t = pre[j] - pre[i];
				ans = max(ans , min(t * k , v[n] + (pre[n] - t) * k) + v[i] - v[j] + 1);
			}
			else {
				int t = pre[i] - pre[j];
				ans = max(ans , min(t * k , -(v[n] - (pre[n] - t) * k)) + v[i] - v[j] + 1);
			}
	}
	write(ans);
	return 0;
}

CF1680E Moving Chips

真的很简单。最后只有一个薯片。

所以左右交汇。定义 \(f_{i,0/1}\) 表示左边 \(i\) 个合在上/下面。

定义 \(g_{i , 0/1}\) 同理。枚举合并位置即可。

int n , f[MAXN][2] , g[MAXN][2] , pt[MAXN] , st[MAXN];
char s[2][MAXN];
 
int main() {
	int T;
	read(T);
	while(T -- > 0) {
		read(n);
		scanf("%s" , s[0] + 1);
		scanf("%s" , s[1] + 1);
		int ty = 0;
		for (int i = 1; i <= n; ++i) {
			if(!ty) {
				f[i][0] = (s[1][i] == '*');
				f[i][1] = (s[0][i] == '*');
			}
			else {
				f[i][0] = min(f[i - 1][1] + 2 , f[i - 1][0] + 1 + (s[1][i] == '*'));
				f[i][1] = min(f[i - 1][0] + 2 , f[i - 1][1] + 1 + (s[0][i] == '*'));
			}
			ty |= (s[1][i] == '*' || s[0][i] =='*');
			pt[i] = ty;
		}
		ty = 0;
		st[n + 1] = 0;
		for (int i = n; i >= 1; --i) {
			if(!ty) {
				g[i][0] = (s[1][i] == '*');
				g[i][1] = (s[0][i] == '*');
			}
			else {
				g[i][0] = min(g[i + 1][1] + 2 , g[i + 1][0] + 1 + (s[1][i] == '*'));
				g[i][1] = min(g[i + 1][0] + 2 , g[i + 1][1] + 1 + (s[0][i] == '*'));
			}
			ty |= (s[1][i] == '*' || s[0][i] =='*');
			st[i] = ty;
		}
		int ans = 1e9;
		for (int i = 0; i <= n; ++i) {
			ans = min(ans , min(f[i][0] + g[i + 1][0] , f[i][1] + g[i + 1][1]) + 1 * pt[i] * st[i + 1]);
		}
		write(ans);
	}
	return 0;
}

CF1680F Lenient Vertex Cover

这种题,我们一般立足于 dfs 树分析。

如果 dfs 树上通过返祖边只能得到偶环,那么说明是二分图,直接染色就好了。

如果只有一个返祖边构成的奇环,那么图上所有奇环都经过返祖边,钦定这条边上两个点颜色一样染色即可。

如果有多个返祖边构成的奇环,那么我们需要找到一条边,使得这条边被所有这样的奇环包含并且不被任何一条偶环包含。

因为如果被奇环和偶环同时包含,那就必然存在不经过它的奇环。找到了钦定边上两点颜色再染色就好。

#include <map>
#include <queue>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <iostream>
#include <algorithm>
#define pii pair <int , int> 
#define mp make_pair
#define fs first
#define sc second
using namespace std; 
typedef long long LL;
typedef unsigned long long ULL;

template <typename T>
void read(T &x) {
	x = 0;T f= 1;char s=getchar();
	while(s<'0'||s>'9') {if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') {x=(x<<3)+(x<<1)+(s^'0');s=getchar();}
	x *= f;
}

template <typename T>
void write(T x , char s='\n') {
	if(!x) {putchar('0');putchar(s);return;}
	if(x<0) {putchar('-');x=-x;}
	int tmp[25] = {} , t = 0;
	while(x) tmp[t ++] = x % 10 , x /= 10;
	while(t -- > 0) putchar(tmp[t] + '0');
	putchar(s);
}

const int MAXN = 2e6 + 5;

int n , m , head[MAXN] , to[MAXN << 1] , nxt[MAXN << 1] , cnt , edge[MAXN << 1] , fl[MAXN << 1];
void add(int u , int v) {nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v;}

int odd , nud[MAXN] , nue[MAXN] , dfn[MAXN] , num , dep[MAXN];
pii tmp;
void dfs(int x , int fa) {
	dfn[x] = ++num;
	for (int i = head[x]; i; i = nxt[i]) {
		int v = to[i];
		if(v == fa) continue;
		if(dfn[v]) {
			if(dfn[v] > dfn[x]) continue;
			if((dep[x] - dep[v] + 1) & 1) {
				odd ++;tmp = mp(x , v);
				nud[x] ++ , nud[v] --;
			}
			else {
				nue[x] ++ , nue[v] --;
			}
		}
		else {
			dep[v] = dep[x] + 1;
			dfs(v , x);
			edge[i] += nud[v];
			if(nue[v]) fl[i] = 1;
			nud[x] += nud[v] , nue[x] += nue[v];
		} 
	}
}

int vis[MAXN];
void find(int x) {
	if(vis[x]) return;
	vis[x] = 1;
//	cerr << x << endl;
	for (int i = head[x]; i; i = nxt[i]) {
		int v = to[i];
		if(dfn[v] < dfn[x]) continue;
		if(!fl[i] && edge[i] == odd) {
			tmp = mp(x , v);
			break;
		}
		find(v);
		if(tmp.fs != -1) break;
	}
}

int col[MAXN];
void calc(int x) {
	for (int i = head[x]; i; i = nxt[i]) {
		int v = to[i];
		if(!col[v]) {
			col[v] = -col[x];
			calc(v);
		}
	}
}

int main() {
//	freopen("1.in" , "r" , stdin);
//	freopen("1.out" , "w" , stdout);
	int T; 
	read(T);
	while(T -- > 0) {
		read(n),read(m);
		for (int i = 1; i <= m; ++i) {
			int u , v;
			read(u),read(v);
			add(u , v) , add(v , u);
		}
		tmp = mp(-1 , -1);
		dfs(1 , 0);
		if(!odd) {
			col[1] = 1;
			calc(1);
			puts("YES");
			for (int i = 1; i <= n; ++i) {
				if(col[i] == 1) putchar('1');
				else putchar('0');
			}
			puts("");
		}
		else if(odd == 1) {
			col[tmp.fs] = col[tmp.sc] = 1;
			calc(tmp.fs) , calc(tmp.sc);
			puts("YES");
			for (int i = 1; i <= n; ++i) {
				if(col[i] == 1) putchar('1');
				else putchar('0');
			}
			puts("");
		}
		else {
			tmp = mp(-1 , -1);
			find(1);
			if(tmp.fs == -1) puts("NO");
			else {
				puts("YES");
				col[tmp.fs] = col[tmp.sc] = 1;
				calc(tmp.fs) , calc(tmp.sc);
				for (int i = 1; i <= n; ++i) {
					if(col[i] == 1) putchar('1');
					else putchar('0');
				}
				puts("");
			}
		}
		for (int i = 1; i <= cnt; ++i) fl[i] = edge[i] = 0;
		num = odd = cnt = 0;
		for (int i = 1; i <= n; ++i) head[i] = 0 , col[i] = 0 , dfn[i] = dep[i] = nud[i] = nue[i] = 0 , vis[i] = 0;
	}
	
	return 0;
}
posted @ 2022-06-25 14:35  Reanap  阅读(47)  评论(0编辑  收藏  举报