3896. 【NOIP2014模拟10.26】战争游戏

 

在这里插入图片描述
鉴于如此一道恶心的题,作者还花了一个晚上草草学了tarjan。
在这里插入图片描述
在这里插入图片描述
于是乎,这道题就是道tarjan
具体怎么实现呢?正解上有个什么树形DP,看的我一脸懵逼。
这道题可以运用到tarjan一个高科技的算法叫——割点。
这里就不再介绍怎么打tarjan了,切入正题。
我们先回忆下割点。
也就是在一个无向图中,将一个点G及其相关的边全部扔掉,会使这个图不在联通,便称点G为割点(作者个人理解)
看完上面的,是不是感觉和题目大意有点相像?
在这里插入图片描述
我先在做tarjan的同时可以很轻松的求得其子节点的数量,从而得知联通块的大小(如蓝色圆圈紫色圆圈橙色圆圈)然后很自然的就能求出子节点的方案数
由上面,也很容易推出绿色圆圈}的方案数

#include<cstdio>
#include<algorithm>
#define N 50001
using namespace std;
int n,m,i,x,y,to,t,low[N],ans[N],dfn[N],last[200001],num[N];
struct node
{
	int go,last;
}p[200001];
void make(int x,int y){p[++t].go=y;p[t].last=last[x];last[x]=t;}
void tarjan(int x)
{
	num[x]=1; dfn[x]=low[x]=++to;
	int tot=0;
	for (int i=last[x];i;i=p[i].last)
	{
		int y=p[i].go;
		if (!dfn[y])
		{
			tarjan(y);
			low[x]=min(low[x],low[y]);
			if (dfn[x]<=low[y])
			{
				ans[x]+=num[y]*(n-1-num[y]);
				tot+=num[y];
			}
			num[x]+=num[y];
		}else low[x]=min(low[x],dfn[y]);
	}
	ans[x]+=tot*(n-1-tot);
}
int main()
{
	freopen("a.in","r",stdin);
	scanf("%d%d",&n,&m);
	for (i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		make(x,y); make(y,x);
	}
	to=0;
	tarjan(1);
	for (i=1;i<=n;i++) printf("%d\n",ans[i]/2+n-1);
}

posted @ 2019-01-25 21:47  Sport_River  阅读(135)  评论(0编辑  收藏  举报