题外话:node
说真的这是一道不写篇博客都对不起我半天debug的时间c++
真的顺手太可怕了数组
正着删点而后每次判联通的复杂度仍是很高的,显然不是这么作的spa
这个时候咱们须要逆向思惟(√debug
考虑从最终状态出发,将删点变成增长点,好像复杂度要低了那么一点点(大概是一大点点吧日志
利用一个标记数组判断当前点是否尚未被加入图中,首先利用并查集计算最终状态的连通块个数,code
而后倒序向图中加点:get
首先先把某个点扔进图中,ans++;博客
而后对这个点的全部边进行一波操做,判断是否是会和其余的连通块一块儿(并查集),变成大联通块(ans--)it
最后倒序输出就行了
是离线作法?
#include<bits/stdc++.h> using namespace std; inline int read(){ int ans=0; char last=' ',ch=getchar(); while(ch>'9'||ch<'0') last=ch,ch=getchar(); while(ch>='0'&&ch<='9') ans=(ans<<1)+(ans<<3)+ch-'0',ch=getchar(); if(last=='-') ans=-ans; return ans; } const int mxn=400010; int n,m,k; int ans,p[mxn],P,fa[mxn]; bool bj[mxn]; int K[mxn]; struct node { int to,nxt; }e[mxn<<1]; int head[mxn],ecnt; void add(int from,int to) { ++ecnt; e[ecnt].nxt=head[from]; head[from]=ecnt; e[ecnt].to=to; } int find(int x) { if(fa[x]!=x) fa[x]=find(fa[x]); return fa[x]; } int main() { n=read(); m=read(); for(int i=1,x,y;i<=m;i++) { x=read(); x++; y=read(); y++; add(x,y); add(y,x); } k=read(); for(int i=1,a;i<=k;i++) { a=read(); K[i]=++a; bj[K[i]]=1; } for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=n;i++) { if(bj[i]) continue; for(int j=head[i],v;j;j=e[j].nxt) { v=e[j].to; if(bj[v]) continue; int u=find(v); int I=find(i); if(u!=I) fa[u]=I; } } for(int i=1;i<=n;i++) if(bj[i]==0&&fa[i]==i) ans++; p[++P]=ans; for(int i=k;i>=1;i--) { bj[K[i]]=0;ans++; for(int j=head[K[i]],v;j;j=e[j].nxt) { v=e[j].to; if(bj[v]==1) continue; int u=find(v); int z=find(K[i]); if(u!=z) { ans--; fa[u]=z; } } p[++P]=ans; } for(int i=P;i>=1;i--) printf("%d\n",p[i]); return 0; }
1.把K[]开成了bool数组,还觉得本身读入读炸了
2.思路bug,傻不愣登的觉得将K[i]点和某个连通块合并之后,若是又有一个属于这个连通块的点与K[i]有边,会影响答案。而后考虑丢点的时候,直接用head[K[i]]!=0判断是否是又自成一连通块了,忘记它有的点还没加进来。
3.顺手什么的太恶心了。习惯于
for(int i=head[u],v;i;i=e[i].nxt) v=e[i].to;
的写法,因而就写成了:
for(int j=head[u],v;j;;j=e[i].nxt) v=e[i].to;