hdu4303

/*
分析:
    dfs,也算是树形dp吧。
    难度一般,不过细节部分处理的我快要吐血了。。。
    以前碰到过这个题,不过想出来的方法觉得麻烦,细节
没有处理好,就搁置了。。。后来又碰到了、又又碰到了、、
又又又碰到了。。。擦!不能忍了!真的不能忍了!遂继续
琢磨自己的方法,发现也没有别的更好的方法了(至少咱这
菜鸟木有发现囧~),然后狠心灭之~
    方法倒是很好想到,求ans的时候一遍dfs就行了,处理
当前node怎么合并其每一个一代儿子了,由于还要判断到儿
子的边的颜色,O(n^2)的挑出来俩比较比较、再挑出来俩比
较比较铁定不行的,所以要先对到儿子的边、按照颜色升序
(或降序)排序,然后合并既可。

                                                     2013-04-16
*/







#include"iostream"
#include"cstdio"
#include"vector"
#include"cstring"
#include"algorithm"
using namespace std;
const int N=300111;

int n,val[N];
struct node{
	int cnt;		//到达node的可行边的数量
	__int64 sum,ans;//sum:到达node的可行边的价值;ans:node节点含其所有儿子、孙子、儿子的孙子、。。。的ans
}E[N];

struct Eage{
	int f,t,c,next;
}eage[2*N],eage2[N];
int tot,tot2,head[N],head2[N];
void add(int a,int b,int c){
	eage[tot].f=a;eage[tot].t=b;eage[tot].c=c;eage[tot].next=head[a];head[a]=tot++;
}
void add2(int a,int b,int c){
	eage2[tot2].f=a;eage2[tot2].t=b;eage2[tot2].c=c;eage2[tot2].next=head2[a];head2[a]=tot2++;
}

int cmp(Eage n1,Eage n2){
	return n2.c<n1.c;
}
int hash[N],num[N];
void dfs0(int s)				//这个函数记录每个节点的儿子的个数,为了:二次建边以及对儿子节点按照颜色排序
{
	int j,v;
	num[s]=0;
	hash[s]=1;
	for(j=head[s];j!=-1;j=eage[j].next)
	{
		v=eage[j].t;
		if(hash[v])	continue;
		num[s]++;
		dfs0(v);
	}
}
void dfs1(int s)				//对儿子排序
{
	int j,v;
	int sum=0;
	vector<Eage> ttemp(num[s]);
	hash[s]=1;
	for(j=head[s];j!=-1;j=eage[j].next)
	{
		v=eage[j].t;
		if(hash[v])	continue;
		ttemp[sum++]=eage[j];
		dfs1(v);
	}
	sort(ttemp.begin(),ttemp.end(),cmp);
	for(j=0;j<sum;j++)	add2(s,ttemp[j].t,ttemp[j].c);
}
void build_map()
{
	int i;
	int a,b,c;
	tot=0;
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c);add(b,a,c);
	}
	memset(hash,0,sizeof(hash));
	dfs0(1);
	tot2=0;
	memset(hash,0,sizeof(hash));
	memset(head2,-1,sizeof(head2));
	dfs1(1);
}

//返回node del,代表儿子的儿子到达儿子的边中,有哪些不可以用来到达当前node
node dfs2(int s,int pre)				//pre是连到这个node(既s)的边的颜色
{
	node now,del;
	if(head2[s]==-1)
	{
		E[s].ans=E[s].sum=E[s].cnt=0;
		now.sum=now.cnt=0;
		return now;
	}
	int j,v,ppre=-1,cnt_temp,que_cnt;	//ppre是当前node的前一个儿子的颜色;俩cnt是为了处理连续出现多个颜色一样的儿子
	__int64 temp,temp2,sum_temp,que_sum;//俩sum也是为了处理连续出现多个颜色一样的儿子
	cnt_temp=que_cnt=now.sum=now.cnt=0;
	sum_temp=que_sum=E[s].ans=E[s].sum=E[s].cnt=0;
	for(j=head2[s];j!=-1;j=eage2[j].next)
	{
		v=eage2[j].t;
		del=dfs2(v,eage2[j].c);
		if(eage2[j].c==pre)
		{
			now.cnt+=E[v].cnt-del.cnt+1;
			now.sum+=E[v].sum-del.sum+(E[v].cnt-del.cnt+1)*val[s]+val[v];
		}

		if(head2[eage2[j].t]==-1)
		{
			temp=1;
			temp2=val[s]+val[v];
		}
		else
		{
			temp=E[v].cnt-del.cnt+1;
			temp2=E[v].sum-del.sum;
			temp2+=temp*val[s]+val[v];
		}

		E[s].cnt+=(int)temp;
		E[s].sum+=temp2;
		E[s].ans+=E[v].ans+temp2;
		if(eage2[j].c==ppre)
		{
			E[s].ans+=sum_temp*temp+cnt_temp*(E[v].sum-del.sum+val[v]);
			que_cnt+=temp;
			que_sum+=temp2;
		}
		else
		{
			sum_temp+=que_sum;
			cnt_temp+=que_cnt;
			E[s].ans+=sum_temp*temp+cnt_temp*(E[v].sum-del.sum+val[v]);
			ppre=eage2[j].c;
			que_cnt=temp;
			que_sum=temp2;
		}
	}
	return now;
}
int main()
{
	int i;
	while(scanf("%d",&n)!=-1)
	{
		for(i=1;i<=n;i++)	scanf("%d",&val[i]);
		if(n==1)	{printf("0\n");continue;}
		build_map();
		memset(E,0,sizeof(E));
		dfs2(1,-1);
		printf("%I64d\n",E[1].ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值