題外話: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;