[SDOI2018]战略游戏

省选临近,放飞自我的小Q无心刷题,于是怂恿小C和他一起颓废,玩起了一款战略游戏。

这款战略游戏的地图由n个城市以及m条连接这些城市的双向道路构成,并且从任意一个城市出发总能沿着道路走到

任意其他城市。现在小C已经占领了其中至少两个城市,小Q可以摧毁一个小C没占领的城市,同时摧毁所有连接这

个城市的道路。只要在摧毁这个城市之后能够找到某两个小C占领的城市u和v,使得从u出发沿着道路无论如何都不

能走到v,那么小Q就能赢下这一局游戏。

小Q和小C一共进行了q局游戏,每一局游戏会给出小C占领的城市集合S

你需要帮小Q数出有多少个城市在他摧毁之后能够让他赢下这一局游戏。

广义圆方树:

就是用一个点把一个点双内的所有点给挂起来,其实就是了利用点双的性质用一个点强行去整合这个点双内的信息

解析:首先这道题是我做过的为数不多的一道圆方树题(基本没有

我们考虑一下题目要求的发现我们直接用虚树将小C所占领的城市整合起来,然后思考一下发现实际上就是求这颗虚树(实际上是原图的那个子图)的割点的个数,这怎么办,再用tarjan?,这显然就不符合虚树本质的目的了,也会导致TLE

这时候圆方树就上场了(咦?为什么我们会想到用圆方树呢?),用完圆方树之后就会发现我们所求的答案就是虚树中圆点的个数(这个圆点不能是原来小C占领的城市)

为什么会想到用圆方树呢?:我们思考一下如果某个点满足要求(即在该子图(其实是个诡异的图)下是个割点),那么这个点一定满足在原图中是一个割点,如果原图中的某个割点被计入答案,那么是不是一定是小C所占领的城市中至少有一对点在该割点所引出的不同点双中,所以再进一步思考就会得到圆方树

#include<bits/stdc++.h>
using namespace std;
const int MAXN=3e5+10;
struct node{
	int u,to;
}edge[MAXN<<1],edge1[MAXN<<2];
int head[MAXN],low[MAXN],dfn[MAXN],a[MAXN],head1[MAXN];
int f[MAXN][20];
int n,m,q,s,k,x,y,T,k1,dep[MAXN],sum[MAXN],sta[MAXN],dfstime,top,p;
void add(int x,int y)
{
	edge[k].u=y;
	edge[k].to=head[x];
	head[x]=k++;
}
void add1(int x,int y){
	edge1[k1].u=y;
	edge1[k1].to=head1[x];
	head1[x]=k1++;
}
bool cmp(int x,int y)
{
	return dfn[x]<dfn[y];
}
void tarjan(int now,int fa)
{
	low[now]=dfn[now]=++dfstime;
	sta[++top]=now;
	for (int i=head[now];i!=-1;i=edge[i].to)
	{
		int u=edge[i].u;
		if (u==fa) continue;
		if (dfn[u]) low[now]=min(low[now],dfn[u]);
		else 
		{
			tarjan(u,now);
			low[now]=min(low[now],low[u]);
			if (low[u]>=dfn[now])
			{
				n++;
				while (sta[top]!=u) 
				{
					add1(n,sta[top]); add1(sta[top],n);
					top--;
				}
				top--; add1(n,u); add1(u,n);
				add1(now,n); add1(n,now);
			}
		}
	}
}
void dfs(int now,int fa,int d)
{
	f[now][0]=fa;
	for (int i=1;i<=19;i++) f[now][i]=f[f[now][i-1]][i-1];
	sum[now]=sum[fa]+(now<=p?1:0);
    dep[now]=d;
    dfn[now]=++dfstime;
	for (int i=head1[now];i!=-1;i=edge1[i].to)
	{
		int u=edge1[i].u;
		if (u==fa) continue;
		dfs(u,now,d+1);
	}
}
int get_lca(int x,int y)
{
	if (dep[x]<dep[y]) swap(x,y);
	for (int i=19;i>=0;i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i];
	if (x==y) return x;
    for (int i=19;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
int  solve(int n)
{
	int top=0;
	int ans=0;
	for (int i=1;i<=n;i++)

	{
        if (!top) sta[++top]=a[i];
        else {
        	int lca=get_lca(a[i],sta[top]);
        	while (dep[sta[top]]>dep[lca]&&top) 
        	{
        		top--; 
        	}
            ans+=sum[a[i]]-sum[lca]-1;
            if (top==0) 
            {
            	ans+=sum[sta[1]]-sum[lca];
            	if (sta[1]<=p) ans--;
            	if (lca<=p) ans++;
            	sta[++top]=lca;
            }
            else if (lca!=sta[top]) sta[++top]=lca;
            sta[++top]=a[i];
        }
	}
	return ans;
}
int main()
{
	scanf("%d",&T);
	while (T--)
	{
		scanf("%d%d",&n,&m);
		p=n;
		for (int i=1;i<=n;i++) head[i]=-1;
		for (int i=1;i<=(n<<1);i++) head1[i]=-1;
		for (int i=1;i<=n;i++) low[i]=dfn[i]=0;
		k=0; dfstime=0; top=0; k1=0;
		for (int i=1;i<=m;i++){
			scanf("%d%d",&x,&y); add(x,y); add(y,x);
		}
        tarjan(1,0);
        dfstime=0;
        dfs(1,0,1);
        scanf("%d",&q);
        for (int i=1;i<=q;i++)
        {
            scanf("%d",&s);
            for (int j=1;j<=s;j++) scanf("%d",&a[j]);
            sort(a+1,a+s+1,cmp);
            printf("%d\n",solve(s));	
        }
	}
}

(本篇文章仅写给那些已经对于圆方树有了解的人,且上述观点仅为博主自己的看法,如有不对请大佬指出)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值