[AGC008F Black Radius] [特征法统计+树形DP]

这题真的神啊……

[思路]

    先画一画样例,在脑海里形成一个比较形象的认识。比如这里先约定一些名词:

    [中心] 就是我们选中的那个点

    [步长] 就是我们染色时确定的范围d

    [边缘] 可以看到,从中心往外扩散,有些路径扩散到了叶子结点,还有些没到尽头就停止了,我们把没到尽头的路径末端结点称为边缘。

    [子树T(x,y)] 以x为树根时,以y为根的子树

    [好点] 可以作为中心进行染色的点

    开始考虑怎么统计。首先直接统计肯定不行,会重复计算。这时一种经典方法就有用了:给每一种方案一个独一无二的表示方法(即特征),然后统计特征个数就可以了。

    我们考虑在树上选了一个点x作为中心,扩散了d步,染黑的点集记为s(x,d),考虑移动这个x,改变d,以得到相同的点集。观察发现我们应当注意这些:如果x存在>=2个相邻结点j,满足T(x,j)中含有边缘,那么x一定不能向这些子树移动。因为x此时一定在两个边缘之间路径的中点处,一旦移动就永远不会成为中心了。可以发现x可以向其它子树随便移动,只要移动一步,让d增加1,就可以再次达到所有边缘。观察到这里,我们已经可以论证出这个性质:对于一个染色图案s,能够形成它的中心必定是树上连续的一块点,并且这些点中存在一个中心点x,d最小,从它往外走,d依次+1,得到其它所有方案。这样我们不就能以这个中心点x作为方案s的“特征”了吗?我们称x为方案s的特征点。

    所以我们现在的思路就确定了:枚举中心x,统计有多少d,使得s(x,d)的特征点恰好为x。这样,每种方案恰好会被它的特征点统计到一次,不会重复计算(我们先做subtask,假设全是好点)。发现整棵树全黑这种情况有点特殊,所以我们把它放一边,最后答案+1。由于x是特征点,所以任意的相邻点j,都有s(j,d-1)≠s(x,d),可以发现,要想对j满足该条件,只要保证s(j,d-1)中,T(j,x)没有完全染黑即可(*),直接树形DP求出T(j,x)里最深的点的深度dep(x深度为0),那么有:d<=dep+1。另外记x往外最远的点和它距离为far[x],那么显然d<far[x](否则树就全黑了)。这样x为特征的方案数就是Dmax+1(0~Dmax都可以)。

    至此,我们就完成了subtask的任务。但是有些点并不是好点,怎么办呢?我们一定要弄清一件事:这个点不是好点,不代表不能作为特征点进行统计。我们只要保证统计的时候,这个方案确实会出现就可以了。这就需要我们找到另一个方案s(x',d'),其中x'是好点,来证明方案s(x,d)确实可以出现。根据(*)的内容可知,从x'移动到x的过程中,子树T(p,x')始终是全部染黑的(p是x'向x走一步到达的相邻点,显然p也可以作为中心,表示出s),也就是说,假如x'属于T(x,j),其中j是x的相邻点,那么T(x,j)必定全被染黑了,这个时候s(x,d)这个不合法的方案才可以用s(x',d')来替代,假设T(x,j)中最深的点深度为dep,则d>=dep时条件成立。假设所有x'中得到的dep最小值是Min,那么我们就确定了d的下界Dmin=Min,即d>=Dmin时,可以找到替代方案s(x',d')以确保s(x,d)合法,至此,我们就不需要关心x是不是好点了,确定d的上界Dmax的方法和之前一样,贡献的方案数就是(Dmax-Dmin+1)。这里面的数据也可以用树形DP推出,很简单的。

    综上,复杂度O(N),完美解决问题,呼……累死了……

#include <cstdio>
#include <cstring>
#include <algorithm>
#define rep(i,j,k) for (i=j;i<=k;i++)
using namespace std;
const int N=2e5+5,INF=1e9;
int n,u,v,i,j,lim,bot;
long long ans;
char s[N];
int deep[N*2],key[N*2],far[N]; //第i条边指向的子树的最深路径长度 是否有关键点 离i最远的点的距离 i出发最远的含有关键点的路径长度(点数) 
int En,size[N],fst[N],nxt[N*2],to[N*2];
void add(int u,int v) {
	En++; nxt[En]=fst[u]; fst[u]=En; to[En]=v;
}
int add(int x)
{
	if (x==INF) return x;
	return x+1;
}
void dfs1(int x,int fa)
{
	int j;
	for (j=fst[x];j;j=nxt[j])
	if (to[j]!=fa)
	{
		dfs1(to[j],x);
		key[j]=size[to[j]];
		far[x]=max(far[to[j]]+1,far[x]);
		size[x]+=size[to[j]];
	}
	if (s[x]=='1') size[x]++;
}
void updata(int dt,int &fs,int &sc)
{
	if (dt>fs) sc=fs,fs=dt;
	else if (dt>sc) sc=dt;
}
int get(int x,int fs,int sc)
{
	if (fs==sc) return fs;
	if (x==fs) return sc;
	return fs;
}
void dfs2(int x,int fa,int abv,int edge)
{
	int j,v,fs=abv+1,sc=0,_abv;
	deep[edge^1]=abv;
	far[x]=max(far[x],abv+1);
	for (j=fst[x];j;j=nxt[j])
	if (to[j]!=fa)
	{
		v=to[j];
		updata(far[v]+1,fs,sc);
	}
	for (j=fst[x];j;j=nxt[j])
	if (to[j]!=fa)
	{
		v=to[j];
		_abv=get(far[v]+1,fs,sc);
		key[j^1]=size[1]-size[v];
		deep[j]=far[v];
		dfs2(v,x,_abv,j);
	}
}
int main()
{
//	freopen("radius.in","r",stdin);
//	freopen("radius.out","w",stdout);
	scanf("%d",&n);
	En=1;
	rep(i,1,n-1)
	{
		scanf("%d%d",&u,&v);
		add(u,v); add(v,u);
	}
	scanf("%s",s+1);
	dfs1(1,1);
	dfs2(1,1,-1,0);

	rep(i,1,n)
	{
		lim=far[i]-1;
		for (j=fst[i];j;j=nxt[j])
			lim=min(lim,deep[j^1]+1);
		if (s[i]=='1') bot=0;
		else {
			bot=INF;
			for (j=fst[i];j;j=nxt[j])
				if (key[j]>0) bot=min(bot,deep[j]+1);
		}
		if (lim>=bot) ans+=lim-bot+1;
	}
	printf("%lld\n",ans+1);
	return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值