高数题 (树形DP?)

【问题描述】
π姐最近生了个漂亮宝宝休假在家,听lee姐介绍经验时发觉lee姐比高中反应迟钝了些,一问才知lee姐是因为连生了两儿子才这样的,lee姐的体会是一孕笨三年,π姐听了觉得闲着也是闲着,得找些挑战的事情做做,于是π姐最近开始刷起了高数题,她遇到了这样一道高数题。这道高数题里面有一棵N个点的树,树上每个点有点权,每条边有颜色。一条路径的权值是这条路径上所有点的点权和,一条合法的路径需要满足该路径上任意相邻的两条边颜色都不相同。问这棵树上所有合法路径的权值和是多少啊?(无向路径)
【输入格式】
第一行一个整数N,代表树上有多少个点。
接下来一行N个整数,代表树上每个点的权值。
接下来N-1行,每行三个整数S、E、C,代表S与E之间有一条颜色为C的边。
【输出格式】
一行一个整数,代表所求的值。
【输入样例】
6
6 2 3 7 1 4
1 2 1
1 3 2
1 4 3
2 5 1
2 6 2
【输出样例】
134
【样例解释】
1-2 Value: 8
1-3 Value: 9
1-4 Value:13
1-2-6 Value:12
2-1-3 Value:11
2-1-4 Value:15
2-5 Value:3
2-6 Value:6
3-1-4 Value:16
3-1-2-6 Value:15
4-1-2-6 Value:19
5-2-6 Value:7
【数据规模】
对与30%的数据,1≤N≤1000。
对于另外20%的数据,可用的颜色数不超过109且随机数据。
对于另外20%的数据,树的形态为一条链。
对于100%的数据,1≤N≤3*105,可用的颜色数不超过109,所有点权的大小不超过105。

题解:
这道题看上去很简单它也的确很简单
记录一个sum数组
p a [ i ] 表 示 以 i 为 终 点 的 路 径 总 数 pa[i]表示以i为终点的路径总数 pa[i]i
s u m [ i ] 表 示 以 i 为 终 点 的 所 有 路 径 的 权 值 和 sum[i]表示以i为终点的所有路径的权值和 sum[i]i(从叶子往跟做,前提是相邻的边的颜色要不一样)
这个是处理能往上走的路径方案
然后不能往上走的也就是说以这个节点为中转站的
就是这个节点所有子节点
p a [ i ] ∗ s u m [ j ] + p a [ j ] ∗ s u m [ i ] + p a [ i ] ∗ p a [ j ] ∗ w [ x ] pa[i]*sum[j]+pa[j]*sum[i]+pa[i]*pa[j]*w[x] pa[i]sum[j]+pa[j]sum[i]+pa[i]pa[j]w[x]
i , j , x i,j,x i,j,x分别表示这个节点的任意两个子节点以及该子节点
因为每条新路径他们都要加一次,所以如上公式

结束!

#include<bits/stdc++.h>
using namespace std;
const int maxm=300010;
long long ans=0;
struct node
{
	long long nxt,v,color;
	long long w;
}e[maxm<<1];
long long cnt,head[maxm],w[maxm],pa[maxm],n;
long long sum[maxm],sump[maxm];
void add(int u,int v,int color)
{
	cnt++;
    e[cnt].v=v;
    e[cnt].nxt=head[u];
    e[cnt].color=color;
    head[u]=cnt;
}
void dfs(int x,int fa,int fr)
{
	for (int i=head[x]; i; i=e[i].nxt)
	  { 
	  	int y=e[i].v;
	  	if (y==fa) continue;
	  	dfs(y,x,e[i].color);
	  	if (fr!=e[i].color) 
		    {
			  pa[x]+=pa[y];
              sum[x]+=sum[y]+w[x]*pa[y];
			}else ans+=sum[y]+w[x]*pa[y];
	  }
	for (int i=head[x]; i; i=e[i].nxt)
	  for (int j=e[i].nxt; j; j=e[j].nxt)
	    if (i!=j&&e[i].color!=e[j].color&&e[i].v!=fa&&e[j].v!=fa)
	      {
	      	ans+=pa[e[i].v]*sum[e[j].v]+pa[e[j].v]*sum[e[i].v]+pa[e[i].v]*pa[e[j].v]*w[x];
	      }
	ans-=w[x];
	sum[x]+=w[x];
	pa[x]++;
}
int main()
{
	scanf("%d",&n);
	for (int i=1; i<=n; i++)
	    scanf("%d",&w[i]);
	for (int i=1; i<n; i++)
	  {
	  	int u,v,w;
	  	scanf("%d%d%d",&u,&v,&w);
	  	add(u,v,w);
	  	add(v,u,w);
	  }
	dfs(1,1,-1);
	for (int i=1; i<=n; i++)
	  ans+=sum[i];
	printf("%lld",ans);
}

注意一些细节,long long之类的

初测0,原因是第二个公式的有一个 j j j打成了 i i i,然后还过了样例没发现,还有是记录父亲连接爷爷的边的颜色
e [ i ] . c o l o r e[i].color e[i].color打成了 i i i,居然够了样例,然后时间没来的及造数据
难过qwq

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值