hdu 4303 Hourai Jeweled

从任意一点开始深搜,对其所有的儿子按边的颜色排个序,然后在按这个序深搜和统计每颗子树。

搜索完毕之后向上返回pair<可以延伸到该点且最后一条边与由父节点到该点的边颜色不同的gorgeous边的条数 , 所有这种边分数的总和>。


每次深搜完一个子节点之后,增加的过这一点的gorgeous边的总分数为:

     之前深搜的所有子节点向上返回的边数之和 * 当前子节点返回的分数 +

    之前深搜的所有子节点向上返回的分数之和 * 当前子节点返回的边数 +

    之前深搜的所有子节点向上返回的边数之和 * 当前子节点返回的边数 * 当前点的权。

#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
using namespace std;

#define N 301000

int head[N],idx;

struct edge{

    int v,color,nt;
    edge(){}
    edge(int v,int color,int nt):v(v),color(color),nt(nt){}
}e[N*2];

int n;
int pv[N];
__int64 ans;

bool cmp(int i,int j){
    return e[i].color<e[j].color;
}


void dfs(int u,int f,__int64 &a,__int64 &b){

     vector<int> v1;
     bool flag=0;

    for(int i=head[u];i!=-1;i=e[i].nt){

        if(i==(f^1)) continue;
        v1.push_back(i);
        flag=1;
    }

    a=b=0;

    if(!flag) return;
    sort(v1.begin(),v1.end(),cmp);

    int pre=-1,cnt=0;

    vector<__int64>vx,vy;

     __int64 x,y,num=0,ans1=0,ans2=0,sum=0;

    for(int i=0;i<v1.size();i++){

        int v=e[v1[i]].v,color=e[v1[i]].color;

        dfs(v,v1[i],x,y);

        if(f==-1||color!=e[f].color)  a+=x+1,b+=pv[v]+y+(x+1)*pv[u];
        num+=x+1,sum+=y+pv[v];

        if(pre!=color)
        {
            vx.push_back(x+1);
            vy.push_back(y+pv[v]);
            cnt++;
        }
        else vx[cnt-1]+=x+1,vy[cnt-1]+=y+pv[v];
        pre=color;
    }

    for(int i=0;i<cnt;i++){
        
     ans2+=(vx[i]*pv[u]+vy[i]);
     ans1+=(sum-vy[i])*vx[i]+(num-vx[i])*vy[i]+(num-vx[i])*vx[i]*pv[u];
    }
    ans+=ans2+ans1/2;

}

int main(){

    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++) scanf("%d",pv+i);
        idx=0;
        memset(head,-1,sizeof(head));

        for(int i=1;i<n;i++){

            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            e[idx]=edge(b,c,head[a]);head[a]=idx++;
            e[idx]=edge(a,c,head[b]);head[b]=idx++;
        }
        ans=0;
        __int64 a,b;
        dfs(1,-1,a,b);

        printf("%I64d\n",ans);
    }
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值