1. 程式人生 > 其它 >【洛谷4606】[SDOI2018] 戰略遊戲(廣義圓方樹+虛樹)

【洛谷4606】[SDOI2018] 戰略遊戲(廣義圓方樹+虛樹)

給定一張$n$個點$m$條邊的無向圖。$q$次詢問,每次給出一組關鍵點,求有多少個非關鍵點被刪去後會導致存在關鍵點不連通。

點此看題目

  • 給定一張\(n\)個點\(m\)條邊的無向圖。
  • \(q\)次詢問,每次給出一組關鍵點,求有多少個非關鍵點被刪去後會導致存在關鍵點不連通。
  • 資料組數\(\le10\)\(n,q\le10^5\)

圓方樹+虛樹

口胡起來很簡單。

我們求出原圖的圓方樹,每次詢問建出虛樹,答案就是虛樹上圓點個數-關鍵點數。

注意廣義圓方樹的建法。

程式碼:\(O(\sum|S|)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 100000
#define M 200000
#define LN 19
#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
using namespace std;
int n,m,k,s[2*N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[2*M+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
namespace RST//廣義圓方樹
{
	int ct,ee,lnk[2*N+5];struct edge {int to,nxt;}e[4*N+5];I void Add(CI x,CI y) {add(x,y),add(y,x);}
	int d,dI[2*N+5],dO[2*N+5],D[2*N+5],V[2*N+5],f[2*N+5][LN+1];I void Init(CI x=1)
	{
		RI i;for(i=1;i<=LN;++i) f[x][i]=f[f[x][i-1]][i-1];
		for(dI[x]=++d,i=lnk[x];i;i=e[i].nxt) e[i].to^f[x][0]&&
			(D[e[i].to]=D[f[e[i].to][0]=x]+1,V[e[i].to]=V[x]+(e[i].to<=n),Init(e[i].to),0);dO[x]=d;
	}
	I int LCA(RI x,RI y)
	{
		RI i;for(D[x]<D[y]&&(swap(x,y),0),i=0;D[x]^D[y];++i) (D[x]^D[y])>>i&1&&(x=f[x][i]);
		if(x==y) return x;for(i=LN;~i;--i) f[x][i]^f[y][i]&&(x=f[x][i],y=f[y][i]);return f[x][0];
	}
	I bool cmp(CI x,CI y) {return dI[x]<dI[y];}
	int vis[2*N+5],S[2*N+5];I void Solve()//建虛樹
	{
		RI i;for(i=1;i<=k;++i) vis[s[i]]=1;sort(s+1,s+k+1,cmp);
		RI t,nk=k;for(i=1;i^k;++i) !vis[t=LCA(s[i],s[i+1])]&&(vis[s[++nk]=t]=1);sort(s+1,s+nk+1,cmp);//添上相鄰兩點LCA
		RI T;for(t=(S[T=1]=s[1])<=n,i=2;i<=nk;t+=V[s[i]]-V[S[T]],S[++T]=s[i++]) W(dI[s[i]]>dO[S[T]]) --T;//求出虛樹上點數
		for(writeln(t-k),i=1;i<=nk;++i) vis[s[i]]=0;//減去關鍵點數得到答案;清空
	}
	I void Cl() {d=ee=ct=0;for(RI i=1;i<=2*n;++i) lnk[i]=0;V[1]=1;}
}
int d,dfn[N+5],low[N+5],T,S[N+5];I void Tarjan(CI x=1)//建廣義圓方樹
{
	dfn[x]=low[x]=++d,S[++T]=x;for(RI i=lnk[x],y,t;i;i=e[i].nxt)
	{
		if(dfn[y=e[i].to]) {low[x]=min(low[x],dfn[y]);continue;}//訪問過
		if(Tarjan(y),low[y]>=dfn[x]) {RST::Add(n+(++RST::ct),x);W(RST::Add(n+RST::ct,S[T]),S[T--]^y);continue;}//找到一個點雙
		low[x]=min(low[x],low[y]);
	}
}
int main()
{
	RI Tt,Qt,i,x,y;read(Tt);W(Tt--)
	{
		for(read(n,m),RST::Cl(),d=ee=0,i=1;i<=n;++i) lnk[i]=dfn[i]=0;//清空
		for(i=1;i<=m;++i) read(x,y),add(x,y),add(y,x);Tarjan(),RST::Init();
		read(Qt);W(Qt--) {for(read(k),i=1;i<=k;++i) read(s[i]);RST::Solve();}
	}return clear(),0;
}
敗得義無反顧,弱得一無是處