SP5150 JMFILTER - Junk-Mail Filte(并查集)
直秒并查集。这题的难点就在于怎么删点。如果要删的是叶节点,那还好,直接刨掉即可
如果是中间节点甚至是根节点,那就不好办了.....
solution:
对于独立一个点,我可以用邻接表模拟,然后用并查集维护联通,删点就是普通删点,但是实现难度高,复杂度大,算了,还是想正解吧
正解:对于一个删了的点,我们打个标记,然后开一个新点表示这个点(居然有点像主席树233.....)
于是,动态开点并查集诞生了(口胡)
具体操作在代码里解释
代码:
#include<bits/stdc++.h> using namespace std; const int maxn=2e6+9; int n,m,ans,vis[maxn],fa[maxn],d[maxn],cnt; int T; int find(int x)//普普通通的并查集 { if(fa[x]==x) return x; return fa[x]=find(fa[x]); } void mergy(int x,int y)//普普通通的合并 { int fx=find(x); int fy=find(y); if(fx!=fy) fa[fx]=fy; } void del()//普普通通的清零(多组数据) { memset(vis,0,sizeof(vis)); ans=0; n=0,m=0; } int main() { scanf("%d%d",&n,&m);//普普通通的输入 while(n!=0||m!=0) { T++;//记录组数 cnt=n; for(int i=0;i<n+m;i++)//不太普通的初始化,因为n个点m个操作,极端情况全部都是拆,所以初始化要n+m(提前把动态的点开好) { fa[i]=i; } for(int i=0;i<n;i++) { d[i]=i;//d表示fa里,di代替第i个点 } while(m--) { char ch; cin>>ch; if(ch=='M') { int x,y; scanf("%d%d",&x,&y); mergy(d[x],d[y]); //合并新点 } else { int x; scanf("%d",&x); d[x]=++cnt;//动态开点,覆盖掉原点 } } for(int i=0;i<n;i++) { int t=find(d[i]);//统计答案 if(!vis[t])//桶统计答案 ans++; vis[t]=1; } printf("Case #%d: %d\n",T,ans); del(); scanf("%d%d",&n,&m); } return 0; }
(完)