BZOJ5329 SDOI2018 战略游戏

版权声明:虽然博主很菜 但是转载还是要问问我的哦= =+ https://blog.csdn.net/hanyuweining/article/details/84999123

传送门

补的第一道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;
}

猜你喜欢

转载自blog.csdn.net/hanyuweining/article/details/84999123