首页 > 其他分享 >CF1680F Lenient Vertex Cover 题解

CF1680F Lenient Vertex Cover 题解

时间:2023-10-17 10:45:02浏览次数:42  
标签:fy int 题解 Cover fx siz Lenient f2x f2y

CF1680F Lenient Vertex Cover 题解

这道题和「JOISC 2014 Day3」电压非常类似,或者说就是一道题。

题意就是给你一个图,问能否对所有点黑白染色,允许最多一条边的两个顶点都染成黑色。

黑白染色后其实就是一个二分图,那如果有一条边的两个顶点染成黑色,就是说去掉该边后,剩下的图为二分图,注意这里要保证两个顶点的颜色相同,否则无法同时染成黑色。

那么我们考虑线段树分治来做。具体地,对于第 \(i\) 条边,我们让它的存在时间为 \([0, i-1]\) 和 \([i+1, m+1]\),表示在第 \(i\) 个时刻,只有第 \(i\) 条边没有加上,利用可撤销并查集即可维护是否为二分图。而在 \(m+1\) 时刻,所有边都被加上,用来判断原图是否为二分图。

时间复杂度 \(O(n \log^2 n)\),时限开得很大,可以过。但是其他方法可以做到线性,我太菜力

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;

int read() {
	int x = 0; char ch = getchar();
	while(ch<'0' || ch>'9') ch = getchar();
	while(ch>='0'&&ch<='9') x = x*10+(ch-48), ch = getchar();
	return x;
}
struct Edge {
	int u, v;
} e[N];
int T;
int n, m;


int fa[N<<1], siz[N<<1], top;
struct xwx {
	int x, y, del;
}stk[N<<1];

int find(int x) {
	if(x == fa[x]) return x;
	return find(fa[x]);
}

bool found;
bool vis[N<<1];
struct SegmentTree {
	vector<int> tree[N<<2];
	#define ls tr<<1
	#define rs tr<<1 | 1
	void insert(int tr, int L, int R, int lq, int rq, int id) {
		if(lq == L && R == rq) {
			tree[tr].push_back(id);
			return;
		}
		int mid = (L+R)>>1;
		if(lq<=mid) insert(ls, L, mid, lq, min(mid, rq), id);
		if(mid < rq) insert(rs, mid+1, R, max(mid+1, lq), rq, id);
	}
	void init(int tr, int L, int R) {
		tree[tr].clear();
		if(L == R) {
			return;
		}
		int mid = (L+R)>>1;
		init(ls, L, mid);
		init(rs, mid+1, R);
	}
	void solve(int tr, int L, int R, bool flag) {
		int now = top;
		for(int i:tree[tr]) {
			int fx = find(e[i].u), fy = find(e[i].v);
			if(fx == fy) {
				flag = 0;
			} else {
				int f2x = find(e[i].u+n), f2y = find(e[i].v+n);
				if(fx != f2y) {
					if(siz[fx] > siz[f2y]) swap(fx, f2y);
					fa[fx] = f2y;
					siz[f2y]+=siz[fx];
					stk[++top] = (xwx) {fx, f2y, siz[fx]};
				}
				if(fy != f2x) {
					if(siz[fy] > siz[f2x]) swap(fy, f2x);
					fa[fy] = f2x;
					siz[f2x]+=siz[fy];
					stk[++top] = (xwx) {fy, f2x, siz[fy]};
				}
			}
		}
		if(L == R) {
			if(flag) {
				if(L <= m) {
					int tx = find(e[L].u), ty = find(e[L].v);
					if(tx!=ty) return;//如果去掉一条边,必须保证两个顶点同色。
					vis[tx] = 1;//都染成黑色。
					vis[ty] = 1;
				}
				
				found = 1;
				puts("YES");
				
				for(int i = 1; i<=n; ++i) {
					int fi = find(i), f2i = find(i+n);
					if(!vis[fi] && !vis[f2i]) {
						vis[fi] = 1;//如果这个连通块还没有染色,则让初始节点为黑色。
						putchar('1');
					} else {
						putchar('0'+vis[fi]);
					}
				}
				putchar('\n');
				return;
			}
			return;
		}
		int mid = (L+R)>>1;
		solve(ls, L, mid, flag);
		if(found) return;
		solve(rs, mid+1, R, flag);
		if(found) return;
		while(top > now) {
			xwx tmp = stk[top--];
			fa[tmp.x] = tmp.x;
			siz[tmp.y]-=tmp.del;
		}
	}
}seg;

void init() {
	for(int i = 1; i<=n*2; ++i) {
		fa[i] = i;
		siz[i] = 1;
		vis[i] = 0;
	}
	top = 0;
	found = 0;
	seg.init(1, 1, m+1);
}
int main() {
	T = read();
	while(T--) {
		n = read(), m = read();
		init();
		for(int i = 1;i<=m; ++i) {
			e[i] = (Edge) {read(), read()};
			if(i > 1) seg.insert(1, 1, m+1, 1, i-1, i);
			if(i <=m) seg.insert(1, 1, m+1, i+1 ,m+1, i);
		}
		seg.solve(1, 1, m+1, 1);
		if(!found) {
			puts("NO");
		}
	}
	return 0;
}

标签:fy,int,题解,Cover,fx,siz,Lenient,f2x,f2y
From: https://www.cnblogs.com/frostwood/p/17769151.html

相关文章

  • 题解:CF237D
    题目传送门思路构造\(k\)个集合,使这些集合满足以下性质:集合的并集为\(V\)。对于树\(s\)中的任意一条边\((a,b)\),都能在\(k\)个集合中找到一个集合\(x\)使得\(a,b\inx\)。对于树\(s\)中的任意一个点\(a\),所有在\(k\)个集合中包含了\(a\)的集合构成了......
  • 题解 P7468【[NOI Online 2021 提高组] 愤怒的小 N】
    题解P7468【[NOIOnline2021提高组]愤怒的小N】problem首先是有一个字符串\(S=\texttt{"0"}\),做无限次“将\(S\)的每一位取反接在\(S\)后面”的操作,形如\(S=0110100110010110\cdots\)。另外给一个\(k-1\)次多项式\(f\),求\(\sum_{i=0}^{n-1}S_if(i).\)\(n\leq......
  • 题解 ABC267F【Exactly K Steps】
    (accoders::NOI#5541.醉(intoxicated))题目描述Robin有一棵树,他有\(m\)次询问,每次询问他给你\(u,k\),你需要输出树上的一个节点\(v\)满足\(dist(u,v)=k\),或者报告无解。\(dist(u,v)\)表示树上\(u\)到\(v\)的最短路径的边数。\(n\leq10^5\)solution考虑求出每个......
  • Math teacher's homework 题解
    preface网上的题解看不懂,看代码看懂了:)solution考虑\(\mathrm{x_i}\)的倒数第\(\mathrm{low_i-1}\)位到倒数第\(\mathrm{1}\)位可以乱选(选\(\mathrm{0/1}\)都满足\(\mathrm{x_i\leqm_i}\)),那么就需要\(\mathrm{x_i}\)和\(\mathrm{m_i}\)的第\(\mathrm{1}\)位......
  • YACS 2023年9月月赛 甲组 题解
    题目链接1题目链接2题目链接3榜单终于公布了,这应该是第二长的榜单公布吧。(最长的一次是去年八月,拖到九月开始后才公布) T1是傻逼数据结构不说了吧,对于每个点枚举以他为角的$k\timesk$的四个正方形算一下点的数量,用$cdq$或者扫描线都行。看这个题目编号是$81$,看来是很......
  • 题解整理
    CF1740ACF1740BCF1740DCF1711BCF1253BCF1080BCF1237ACF1743ACF1743CCF1743BCF1370B......
  • P9744 消除序列 题解
    本题有多种解法,我这里先讲一个我的考场做法吧。切入点我们发现我们至多使用一次操作一,而剩下部分的\(0\)肯定是依靠操作二补全,操作三的作用只是用来填补操作一的空白的,所以我们发现我们对一个序列的操作一定是前一段用操作一和操作三,后一段用操作二。思路1一开始考虑暴力\(......
  • CEIT 23练习编程题 题解
    本文部分题目提供c/c++两种解法,顺便可以让你们知道c++在面对某些题时的优势部分题目提供多种解法日期格式化C#include<stdio.h>intmain(){intm,d,y;scanf("%d-%d-%d",&m,&d,&y);printf("%04d-%02d-%02d",y,m,d);return0;}02d的含义:当有效数......
  • 【题解】「KDOI-06-S」补题
    「KDOI-06-S」A.「KDOI-06-S」消除序列赛时写了一个\(O(nq)\)的线性DP,喜提60分。注意到如果操作1被使用,则一定只会使用一次,而且在最优策略中一定是第一次使用操作1。则我们可以通过以下方式进行操作,使序列满足条件:首先执行\(a_i\)和\(\sum^{j\lei,i\inP}_{j=......
  • [COCI2015-2016#4] ENDOR 题解
    [COCI2015-2016#4]ENDOR题解首先要发现一个很重要的性质,那就是两只变色龙碰撞后回头,等效于两只变色龙继续往前走,其中向右走的颜色不变,而向左走的要改变颜色。那这样就有一种\(O(n^2)\)的做法:对于向右的变色龙,直接贡献答案;对于向左的变色龙,我们按照碰到的先后顺序枚举它前面......