把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

luogu P5227 [AHOI2013]连通图

题面传送门
一开始傻子一样的写了个线段树分治+LCT常数巨大只得了\(50\)分和没有按秩合并的并查集一样分数。
然后我们发现这个东西删掉的边数很少,所以如果每一次都把这个东西重新初始化一边很不划算。
然后可以发现有很多边在一大段区间内都是存在的。
所以就可以上线段树分治了,连通性用并查集维护。
这个是\(O(nlog^2n)\)
但是还有更优秀的离线做法:LCT维护删除时间最大生成树。
那么这个就可以做到常数巨大\(O(nlogn)\)了。
code:

#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define beg(x) int cur=s.h[x]
#define end cur
#define go cur=tmp.z
#define l(x) x<<1
#define r(x) x<<1|1
#define N 200039
#define ll long long
using namespace std;
int n,m,k,x[N],y[N],z,c,cnt,last[N],ans[N],un,wn;
vector<int> f[4*N];
int fa[N],st[N],sh,siz[N];
I int find(int x){return fa[x]==x?x:find(fa[x]);}
I void swap(int &x,int &y){x^=y^=x^=y;}
I void merge(int x,int y){
	un=find(x);wn=find(y);
	(un^wn)&&((siz[un]>siz[wn])&&(swap(un,wn),0),st[++sh]=un,fa[un]=wn,siz[wn]+=siz[un],cnt++);
}
I void get(int x,int y,int z,int l=1,int r=k,int now=1){
	if(x<=l&&r<=y) return (void)(f[now].push_back(z));int m=l+r>>1;
	(x<=m)&&(get(x,y,z,l,m,l(now)),0);(y>m)&&(get(x,y,z,m+1,r,r(now)),0);
}
I void find(int l=1,int r=k,int now=1){
	int i,tmp,lasttop=sh;for(i=0;i<f[now].size();i++)tmp=f[now][i],merge(x[tmp],y[tmp]);
	int m=l+r>>1;l^r?(find(l,m,l(now)),find(m+1,r,r(now)),0):(ans[l]=cnt);
	while(sh^lasttop) tmp=st[sh--],siz[fa[tmp]]-=siz[tmp],fa[tmp]=tmp,cnt--;
}
int main(){
	freopen("1.in","r",stdin);
	register int i;scanf("%d%d",&n,&m);
	for(i=1;i<=n;i++) fa[i]=i,siz[i]=1;
	for(i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]),last[i]=1;
	scanf("%d",&k);
	for(i=1;i<=k;i++){
		scanf("%d",&c);while(c--) scanf("%d",&z),(i^last[z])&&(get(last[z],i-1,z),0),last[z]=i+1;
	}
	for(i=1;i<=m;i++)(last[i]!=k+1)&&(get(last[i],k,i),0);find();
	for(i=1;i<=k;i++) printf("%s\n",ans[i]==n-1?"Connected":"Disconnected");
}
posted @ 2021-03-25 18:48  275307894a  阅读(36)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end