BZOJ5329 SDOI2018 戰略遊戲
阿新 • • 發佈:2018-12-22
傳送門
補的第一道SDOI2018?
圓方樹上建虛樹 歡樂多又多【大霧
大概就是求對於S個點 問刪掉一個點使它們不完全連通的方案數
那麼我們可以看出 這個其實就是求兩兩路徑並上的割點數量
那麼 圓方樹來解決是最好的辦法【好像也沒有別的辦法
然後我們發現如果兩兩統計LCA的話,複雜度是S^2無法接受
可以看出 這個過程和虛樹的構建過程十分相似
所以 我們就可以建出虛樹 統計答案即可
附程式碼。
#include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #include<stack> #define inf 20021225 #define ll long long #define mxm 200100 #define mxn 100100 using namespace std; struct edge{int to,lt;}p[mxm<<1],t[mxm<<1],e[mxm<<1]; // p 原圖 t 虛樹 e 圓方樹 int ip[mxn],it[mxn<<1],in[mxn<<1]; int cnp,cntt,cnt,pt,poi[mxm],n,m; int dfn[mxn<<1],low[mxn],dep[mxn<<1],tot; int f[mxn<<1][20],g[mxn<<1]; int stk[mxn<<1],num;bool spc[mxn<<1]; // 注意不要用混!!! void add(int x,int y) { e[++cnt].to=y;e[cnt].lt=in[x];in[x]=cnt; e[++cnt].to=x;e[cnt].lt=in[y];in[y]=cnt; } void app(int x,int y) { p[++cnp].to=y;p[cnp].lt=ip[x];ip[x]=cnp; p[++cnp].to=x;p[cnp].lt=ip[y];ip[y]=cnp; } void att(int x,int y)//只建兒子邊 { t[++cntt].to=y;t[cntt].lt=it[x];it[x]=cntt; } stack<int> tt; void tarjan(int x,int ff) { dfn[x]=low[x]=++tot;tt.push(x); for(int i=ip[x];i;i=p[i].lt) { int y=p[i].to;if(y==ff) continue; if(!dfn[y]) { tarjan(y,x); low[x]=min(low[y],low[x]); if(low[y]>=dfn[x]) { ++num;add(num,x);int tmp; do { tmp=tt.top();tt.pop(); add(num,tmp); }while(tmp!=y); } } else low[x]=min(low[x],dfn[y]); } } void dfs(int x) { dfn[x]=++tot;//記得清零!! for(int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1]; for(int i=in[x];i;i=e[i].lt) { int y=e[i].to;if(dep[y]) continue; f[y][0]=x;g[y]=g[x]+(y<=n); dep[y]=dep[x]+1;dfs(y); } } void maketree()//建圓方樹 { for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i,i); g[1]=dep[1]=1;tot=0;dfs(1); } int LCA(int x,int y) { if(dep[x]<dep[y]) swap(x,y); int len=dep[x]-dep[y]; for(int i=19;~i;i--) if(len&(1<<i)) x=f[x][i]; if(x==y) return x; for(int i=19;~i;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } void insert(int x) { if(!stk[0]){stk[++stk[0]]=x;return;} int lca=LCA(stk[stk[0]],x); if(lca!=stk[stk[0]]) while(stk[0]>1) { int z=stk[stk[0]-1]; if(dfn[z]>dfn[lca]) att(z,stk[stk[0]]),stk[0]--; else if(z==lca){att(z,stk[stk[0]]);stk[0]--;break;} else if(dfn[z]<dfn[lca]){att(lca,stk[stk[0]]),stk[stk[0]]=lca;break;} } if(stk[stk[0]]!=x) stk[++stk[0]]=x; } bool cmp(int x,int y){return dfn[x]<dfn[y];} int ans,rt;//清零!! void build() { sort(poi+1,poi+pt+1,cmp); rt=poi[1];cntt=0; for(int i=2;i<=pt;i++) rt=LCA(rt,poi[i]); stk[0]=0;insert(rt); for(int i=1;i<=pt;i++) insert(poi[i]); while(stk[0]>1) att(stk[stk[0]-1],stk[stk[0]]),stk[0]--; } void query(int x) { for(int &i=it[x];i;i=t[i].lt) { int y=t[i].to; ans+=g[y]-g[x]; query(y); } spc[x]=0; } void init() { memset(in,0,sizeof(in)); memset(ip,0,sizeof(ip)); memset(f,0,sizeof(f)); memset(g,0,sizeof(g)); memset(dep,0,sizeof(dep)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); cnt=cnp=tot=0; while(!tt.empty()) tt.pop(); } int main() { int T,x,y,q; scanf("%d",&T); while(T--) { init(); scanf("%d%d",&n,&m);num=n; for(int i=1;i<=m;i++) scanf("%d%d",&x,&y),app(x,y); maketree(); scanf("%d",&q); for(int i=1;i<=q;i++) { scanf("%d",&pt); for(int j=1;j<=pt;j++) scanf("%d",&poi[j]),spc[poi[j]]=1; build();ans=(rt<=n)?1:0;query(rt);ans-=pt; printf("%d\n",ans); } } return 0; }