Processing math: 100%

[BZOJ 4025] 二分图

题目传送-BZOJ4025

题意:

有一张n个节点的无向图,其中边isi出现,ei结束,并连接着节点x,y.
并保证si<eiT,要求对于每个时间tT输出此时的图是否是二分图。
n100000,m200000,T100000

题解:

这是道好题,考到了线段树(分治)的思想,听说也叫整体二分?
考虑如何做一个区间(时间)[L,R]中的所有答案:
显然我们只考虑与这个区间有交集的边
分两种:完全覆盖这个区间,部分覆盖这个区间。
显然我们可以毫无顾虑的把第一类的边都加上
此时如果已经不是二分图了,那显然直接GG了
然后对于其它边我们只要递归下去处理就行了
如何判断二分图并支持对图的修改:
考虑按秩合并的带权并查集,这就不多说了
复杂度的证明:
空间:
观察发现每个区间的处理需要有一个边集
而每条边最多能在log个区间中出现(线段树的思想)
那么总共的空间需求就是mlog2T
时间:
同样一条边只能与log个区间相关,也就是说它只能被删加log次
所以复杂度也是mlog2T的?
不知道我这样分析对不对,貌似@zhouzhendong大佬说这是log2

过程:

已更新至我都错题集-updating
并查集相关错误

代码:

const int N=100010,M=200010;
int n,m,T;
struct EDGE {
	int x,y,s,t;
	inline void in() {
		read(x); read(y); read(s); read(t); ++s;
	}
};
int ans[N];
int cnt=0;
namespace DSU {
	int val[N],dep[N],fat[N];
	struct TMP {
		int fx,fy,dx,dy;
	};//x merge to y -> dep[fx]<dep[fy]
	stack<TMP> sta;
	inline void Init() {
		for(int i=1;i<=n;i++) 
			fat[i]=i,dep[i]=1,val[i]=0;
	}
	inline pii father(int x) {
		int ret=0;
		while(x!=fat[x]) ret^=val[x],x=fat[x];
		return mp(x,ret);
	}
	inline bool Merge(int x,int y) {
		pii tx=father(x),ty=father(y);
		int fx=tx.F,fy=ty.F,vx=tx.S,vy=ty.S;
		if(dep[fx]>dep[fy]) swap(fx,fy);
		int v=1^vx^vy;
		if(fx==fy) return v==0;
		sta.push((TMP) {fx,fy,dep[fx],dep[fy]});
		fat[fx]=fy; val[fx]=v;
		dep[fy]=max(dep[fy],dep[fx]+1);
		return true;
	}
	inline void Split(int to) {
		--cnt; assert(cnt>=0);
		while((int)sta.size()!=to) {
			// puts("?");
			assert(!sta.empty());
			TMP tmp=sta.top(); sta.pop();
			int fx=tmp.fx,fy=tmp.fy,dx=tmp.dx,dy=tmp.dy;
			dep[fx]=dx; dep[fy]=dy; fat[fx]=fx; val[fx]=0;
		}
	}
}
vector<EDGE> E;
void Solve(int S,int T,vector<EDGE> &E) {
	assert(S<=T);
	if((int)E.size()==0) {
		for(int i=S;i<=T;i++)
			ans[i]=1;
		return;
	}
	++cnt;
	vector<EDGE> EL,ER; EL.clear(); ER.clear();
	int top=DSU::sta.size(),mid=(S+T)>>1;
	for(int i=0;i<(int)E.size();i++) {
		EDGE e=E[i];
		if(e.s<=S && T<=e.t) {
			if(!DSU::Merge(e.x,e.y)) {
				// puts("???");
				for(int j=S;j<=T;j++)
					ans[j]=0;
				DSU::Split(top); return;
			} 
		} else {
			if(e.s<=mid) EL.push_back(e);
			if(e.t> mid) ER.push_back(e);
		}
	}
	if(S==T) {ans[S]=1; DSU::Split(top); return;}
	Solve(S,mid,EL); Solve(mid+1,T,ER);
	DSU::Split(top); return;
}
signed main() {
	// freopen("10.in","r",stdin);
	// freopen("my.out","w",stdout);
	mem(ans,-1);
	read(n); read(m); read(T);
	DSU::Init();
	for(int i=1;i<=m;i++) {
		EDGE e; e.in(); E.push_back(e);
	}
	Solve(1,T,E);
	// for(int i=1;i<=n;i++) printf("%d ",ans[i]);
	for(int i=1;i<=n;i++) puts(ans[i] ? "Yes" : "No");
	return 0;
}
/*
3 3 3
1 2 0 2
2 3 0 3
1 3 1 2
*/

用时:1h

posted @   functionendless  阅读(108)  评论(0)    收藏  举报
编辑推荐:
· 从零实现富文本编辑器#3-基于Delta的线性数据结构模型
· 记一次 .NET某旅行社酒店管理系统 卡死分析
· 长文讲解 MCP 和案例实战
· Hangfire Redis 实现秒级定时任务,使用 CQRS 实现动态执行代码
· Android编译时动态插入代码原理与实践
阅读排行:
· 一天 Star 破万的开源项目「GitHub 热点速览」
· 瞧瞧别人家的日期处理,那叫一个优雅!
· 使用TypeScript开发微信小程序(云开发)-入门篇
· 没几个人需要了解的JDK知识,我却花了3天时间研究
· 定时任务稳定性解决方案-healthchecks监控系统
点击右上角即可分享
微信分享提示