标签:P3144 Farm 查集 int 3001 读入 Closing ss order
算法
并查集+逆序
思路
首先读入相连的点,但这里不能直接合并建立并查集,因为并查集没有Ctrl+Z操作(就是无法分离两个已经合并的集合),所以我们要先存起来,等所有的询问都读入之后,倒着进行操作。
我们考虑怎样倒着操作:
首先,读入数据,把所有的数据都存起来,其中x[i],y[i]表示第i次读入的关系,order[i]表示第i次读入的数是多少,ss[i]表示i是否在并查集里面,如果存在,则为0,不存在则为1。
接着,倒着处理读入的询问。从第i=n次开始,把与点order[i]有关的边读入,合并并查集,之后在把和该点有关的所有的可加入的边都加入并查集以后,判断并查集中集合的个数,并记录在ans[i]中,然后i--,重复以上步骤。
最后,从1开始到n-1,判断ans是否为1,如果为1,说明所有的点都是联通的(只有一个并查集),输出YES,否则输出NO,第n次询问的时候,所有的点都已经从并查集删除,因此一定是联通的,输出YES。
代码
#include<cstdio> #include<cstring> using namespace std; int n,m,g[3001],x[3001],y[3001],order[3001],ss[3001],ans[3001],w; //x[i],y[i]表示第i次读入的关系,order[i]表示第i次读入的数是多少,ss[i]表示i是否在并查集里面,如果存在,则为0,不存在则为1 //g[i]存储并查集,ans[i]存储第i次询问时并查集中有多少个集合。 int find(int u) { if(g[u]!=u)g[u]=find(g[u]); return g[u]; } void merg(int u,int v) { u=find(u); v=find(v); if(u==v)return; g[u]=v; } int main() { memset(ss,0,sizeof(ss)); scanf("%d%d",&n,&m); for(int j=1;j<=n;j++)g[j]=j; for(int i=1;i<=m;i++) scanf("%d%d",&x[i],&y[i]); for(int i=1;i<=n;i++) { scanf("%d",&w); order[i]=w; ss[w]=1;//不在并查集内 } for(int i=n;i>0;i--) { ss[order[i]]=0;//由于倒序,此时它被建造 for(int j=1;j<=m;j++) if(ss[x[j]]==0&&ss[y[j]]==0)//建造后可以合并 merg(x[j],y[j]); ans[n]=0; for(int j=1;j<=n;j++) if(find(j)==j&&ss[j]==0)//为并查集的根(在并查集内且为自己祖先) ans[i]++; } for(int i=1;i<=n-1;i++) if(ans[i]==1)printf("YES\n");//只有一个并查集 else printf("NO\n");//多个并查集不连通 printf("YES"); return 0; }
标签:P3144,Farm,查集,int,3001,读入,Closing,ss,order 来源: https://www.cnblogs.com/ruanmowen/p/12731043.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。