USACO 2016 US Open Contest Gold T2: Closing the Farm
题目大意
FJ和他的奶牛们正在计划离开小镇做一次长的旅行,同时FJ想临时地关掉他的农场以节省一些金钱。
这个农场一共有被用M条双向道路连接的N个谷仓(1<=N,M<=200000)。为了关闭整个农场,FJ 计划每一次关闭掉一个谷仓。当一个谷仓被关闭了,所有的连接到这个谷仓的道路都会被关闭,而且再也不能够被使用。
FJ现在正感兴趣于知道在每一个时间(这里的“时间”指在每一次关闭谷仓之前的时间)时他的农场是否是“全连通的”——也就是说从任意的一个开着的谷仓开始,能够到达另外的一个谷仓。注意自从某一个时间之后,可能整个农场都开始不会是“全连通的”。
题目分析
对于每一个点,我们要关闭它,然后断开所有与它相连的边,并且把它标记为已删除,接着按照题目判断就好了。
事实上,这种没有强制在线的断边似乎都可以并查集搞。
离线后,然后倒着加边,最后倒着回答询问就好了。
主要是对于这个题目,你需要O(1)判断一张图是否联通。
这在并查集如果你枚举每个点算联通块个数的话会超时
不如直接维护个并查集的size,然后判断下当前size是否为图中点数就好了。
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int MAXN=3e5+10; 4 5 vector<int> G[MAXN]; 6 set<int> s; 7 int n,m; 8 int q[MAXN],ans[MAXN]; 9 int f[MAXN],siz[MAXN]; 10 inline int Find(int x){ 11 return f[x]==x?x:f[x]=Find(f[x]); 12 } 13 inline void Merge(int x,int y){ 14 int ex=Find(x),ey=Find(y); 15 if(ex!=ey){ 16 if(siz[ey]<siz[ex]) swap(ex,ey); 17 f[ex]=ey; 18 siz[ey]+=siz[ex]; 19 } 20 } 21 int main(){ 22 scanf("%d%d",&n,&m); 23 for(int i=1;i<=n;++i){ 24 f[i]=i; 25 siz[i]=1; 26 } 27 for(int i=1,u,v;i<=m;++i){ 28 scanf("%d%d",&u,&v); 29 G[u].push_back(v); 30 G[v].push_back(u); 31 } 32 for(int i=1;i<=n;++i) 33 scanf("%d",&q[i]); 34 for(int i=n,u;i>=1;--i){ 35 u=q[i]; 36 s.insert(u); 37 for(int k=0;k<(int)G[u].size();++k) 38 if(s.count(G[u][k])) Merge(G[u][k],u); 39 int rt=Find(*s.begin()); 40 ans[i]=siz[rt]==(int)s.size(); 41 } 42 for(int i=1;i<=n;++i) 43 printf("%s\n",ans[i]?"YES":"NO"); 44 return 0; 45 }