树形dp Civilization QBXT Test Ⅱ T3

17 篇文章 0 订阅
3 篇文章 0 订阅

题意:一棵树, q q q次询问,每次给出一个点 x x x,求到该点距离为奇数的节点的距离和与到该点距离为偶数的节点的距离和。

比较明显的树形dp,但是我不会QAQ。
首先先考虑求树上所有点到一个点距离之和,我们使用两遍 d f s dfs dfs完成,我们设 f [ x ] f[x] f[x]表示 x x x的子树中所有点到该点的距离之和, s i z [ x ] siz[x] siz[x]表示以 x x x为根的子树大小,在第一遍 d f s dfs dfs中,我们用子节点去更新父节点,转移: s i z [ x ] + = s i z [ v ] siz[x]+=siz[v] siz[x]+=siz[v] f [ x ] = f [ v ] + e [ i ] . d i s ∗ s i z [ v ] f[x]=f[v]+e[i].dis*siz[v] f[x]=f[v]+e[i].dissiz[v]

a n s [ x ] ans[x] ans[x]表示节点 x x x的答案,如果以 1 1 1为根的话则 a n s [ 1 ] = f [ 1 ] ans[1]=f[1] ans[1]=f[1]。在第二遍 d f s dfs dfs中,我们用父节点去更新子节点,转移: a n s [ v ] = a n s [ x ] − e [ i ] . d i s ∗ s i z [ v ] + e [ i ] . d i s ∗ ( n − s i z [ v ] ) ans[v]=ans[x]-e[i].dis*siz[v]+e[i].dis*(n-siz[v]) ans[v]=ans[x]e[i].dissiz[v]+e[i].dis(nsiz[v])

这个问题就被解决了,然后考虑原问题,我们发现二者非常类似,只需要将奇数和偶数分开来算即可。

我们设 f [ x ] [ 0 / 1 ] f[x][0/1] f[x][0/1]表示 x x x的子树中所有点到该点的距离为偶数/奇数之和, a n s [ x ] [ 0 / 1 ] ans[x][0/1] ans[x][0/1]表示距离 x x x为偶数/奇数的答案, s i z 1 [ x ] [ 0 / 1 ] siz1[x][0/1] siz1[x][0/1]表示以 x x x为根的子树中到 x x x点为偶数/奇数的点的数量, s i z 2 [ x ] [ 0 / 1 ] siz2[x][0/1] siz2[x][0/1]表示全树中距离点 x x x为偶数/奇数的点的数量。

d f s dfs dfs中,如果该边为奇数,则到这个点的距离的奇偶性就会改变,如果该边为偶数,则不会改变。转移方程类似上面的,只不过要分奇偶讨论。转移方程在代码里,比较清楚。

#include<iostream>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=2e5+10;
int siz1[maxn][2],siz2[maxn][2];
ll ans[maxn][2],f[maxn][2];
int head[1001000],num,n,q;
struct node
{
	int next,to,dis;
}e[1001000];
void add(int from,int to,int dis)
{
	e[++num].next=head[from];
	e[num].to=to;
	e[num].dis=dis;
	head[from]=num;
}
void dfs1(int x,int fa)
{
	siz1[x][0]=1;
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(v!=fa)
		{
			dfs1(v,x);
			if(e[i].dis%2==1)
			{
				siz1[x][0]+=siz1[v][1];
				siz1[x][1]+=siz1[v][0];
				f[x][0]+=f[v][1]+e[i].dis*siz1[v][1];
				f[x][1]+=f[v][0]+e[i].dis*siz1[v][0]; 
			} 
			else
			{
				siz1[x][0]+=siz1[v][0];
				siz1[x][1]+=siz1[v][1];
				f[x][0]+=f[v][0]+e[i].dis*siz1[v][0];
				f[x][1]+=f[v][1]+e[i].dis*siz1[v][1]; 				
			}
		}
	}
}
void dfs2(int x,int fa)
{
	for(int i=head[x];i;i=e[i].next)
	{
		int v=e[i].to;
		if(v!=fa)
		{
			if(e[i].dis%2==1)
			{
				siz2[v][1]=siz2[x][0];
				siz2[v][0]=siz2[x][1];
				ans[v][1]=ans[x][0]-e[i].dis*siz1[v][1]+e[i].dis*(siz2[x][0]-siz1[v][1]);
				ans[v][0]=ans[x][1]-e[i].dis*siz1[v][0]+e[i].dis*(siz2[x][1]-siz1[v][0]);
			}
			else
			{
				siz2[v][1]=siz2[x][1];
				siz2[v][0]=siz2[x][0];
				ans[v][1]=ans[x][1]-e[i].dis*siz1[v][1]+e[i].dis*(siz2[x][1]-siz1[v][1]);
				ans[v][0]=ans[x][0]-e[i].dis*siz1[v][0]+e[i].dis*(siz2[x][0]-siz1[v][0]);				
			}
			dfs2(v,x);
		}
	}
}
int main()
{
	freopen("civilization.in","r",stdin);
	freopen("civilization.out","w",stdout);
	cin>>n>>q;
	for(int i=1;i<=n-1;++i)
	{
		int x,y,d;
		scanf("%d%d%d",&x,&y,&d);
		add(x,y,d);
		add(y,x,d);
	}
	dfs1(1,0);
	siz2[1][1]=siz1[1][1];
	siz2[1][0]=siz1[1][0];
	ans[1][1]=f[1][1];
	ans[1][0]=f[1][0];
	dfs2(1,0);
	for(int i=1;i<=q;++i)
	{
		int x;
		scanf("%d",&x);
		printf("%lld %lld\n",ans[x][1],ans[x][0]);
	}
	return 0;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值