P4606 [SDOI2018]战略游戏 圆方树+虚树

题意:

戳这里

分析:

  • 前置芝士:圆方树,虚树

题意让我们求出,哪些点删掉后会使得点集里某两个点不再联通

暴力 (O(qn^2)) , 就是每次询问直接枚举点, (O(n)) check 一下是否合法

正解:

对于这种图上割点 的问题,我们可以建出圆方树转化在树上去做,我们建出圆方树,利用圆方树的性质:原图上任意两点的割点一定是他们在圆方树上路径上的原点,所以我们只要求出在点集中某两个点的路径上的原点的个数,对于这种给定点集总和的题,我们按照常见思路建出虚树,直接求一下虚树的大小

tip: 对于求虚树大小的这种操作,其实都不需要建出虚树,加边的时候直接统计新加进来多少个节点

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define inl inline
#define reg register

using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 4e5+5;
	const int maxm = 8e5+5;
	int t,n,m,qt,top,col,ans,idx;
	int dfn[maxn],st[maxn],low[maxn],dep[maxn],fa[maxn][20],tmp[maxn];

	struct graph
	{
		int cnt;
		int head[maxn];
		struct edge
		{
			int to,nxt;
		}e[maxm];
		void clear()
		{
			cnt=0;
			memset(head,0,sizeof(head));
		}
		void add(int u,int v)
		{
			e[++cnt].to=v;
			e[cnt].nxt=head[u];
			head[u]=cnt;
		}
		void add_edge(int u,int v)
		{
			add(u,v);add(v,u);
		}
	}t1,t2;

	void tarjan(int u,int ff)
	{
		dfn[u]=low[u]=++idx;
		st[++top]=u;
		for(int i=t1.head[u];i;i=t1.e[i].nxt)
		{
			int v=t1.e[i].to;
			if(v==ff) continue;
			if(!dfn[v])
			{
				tarjan(v,u);
				low[u]=min(low[u],low[v]);
				if(low[v]>=dfn[u])
				{
					t2.add_edge(++col,u);
					for(;st[top+1]!=v;top--) t2.add_edge(col,st[top]);	
				}
			}
			else low[u]=min(low[u],dfn[v]);
		}
	}
	
	void dfs(int u,int ff)
	{
		dfn[u]=++idx;fa[u][0]=ff;dep[u]=dep[ff]+1;
		for(int i=t2.head[u];i;i=t2.e[i].nxt)
		{
			int v=t2.e[i].to;
			if(v==ff) continue;
			dfs(v,u);
		}
	}
	
	int lca(int x,int y)
	{
		if(dep[x]<dep[y]) swap(x,y);
		for(int i=19;i>=0;i--) if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
		if(x==y) return x;
		for(int i=19;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
		return fa[x][0];
	}
	
	bool cmp(int x,int y)
	{
		return dfn[x]<dfn[y];
	}
	
	void work()
	{
		int a,b;
		t=read();
		while(t--)
		{
			t1.clear();t2.clear();for(int i=1;i<=col;i++) dfn[i]=0;
			n=read();m=read();col=n;
			for(int i=1;i<=m;i++)
			{
				a=read();b=read();
				t1.add_edge(a,b);
			}
			top=0;idx=0;tarjan(1,0);
			idx=0;dfs(1,0);
			for(int j=1;j<=19;j++) for(int i=1;i<=col;i++) fa[i][j]=fa[fa[i][j-1]][j-1];
			qt=read();
			while(qt--)
			{
				a=read();ans=0;top=0;
				for(int i=1;i<=a;i++) tmp[i]=read();
				sort(tmp+1,tmp+a+1,cmp);
				for(int i=1;i<=a;i++)
				{
					if(!top) st[++top]=tmp[i];
					else
					{
						int x=lca(st[top],tmp[i]);
						ans+=((dep[st[top]]+1)>>1)-((dep[x]+1)>>1);
						while(top&&dep[st[top]]>=dep[x]) top--;
						st[++top]=x;st[++top]=tmp[i];
					}
				}
				ans+=((dep[st[top]]+1)>>1)-(dep[st[1]]>>1)-a;
				printf("%d
",ans);
			}
		}
	
	}

}

int main()
{
//	freopen("01.in","r",stdin);
//	freopen("ans.out","w",stdout);
	zzc::work();
	return 0;
}

原文地址:https://www.cnblogs.com/youth518/p/14272897.html