【jzoj5071】【GDSOI2017第二轮模拟】【奶酪】【树形动态规划】

题目大意

CJY很喜欢吃奶酪,于是YJC弄到了一些奶酪,现在YJC决定和CJY分享奶酪。
YJC弄到了n-1块奶酪,于是他把奶酪挂在了一棵n个结点的树上,每根树枝上挂一块奶酪,每块奶酪都有重量。
YJC和CJY决定这样分奶酪:首先砍掉一根树枝,把树分成两部分,每人取一部分,然后各自在自己取的那部分树上选择一条路径并取走路径上的奶酪,然后把剩下的奶酪拿去喂老鼠。
两人都想让自己取走总重量尽量大的奶酪,但他们不知道砍掉哪一根树枝最好。所以他们想让你计算,对于每一根树枝,砍掉之后每个人取走的奶酪的总重量的最大值。

解题思路

树形动态规划,枚举删哪一条边,维护往下三条链,往上一条链,子树直径,儿子子树两条直径,各种if即可。

code

#include<set>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LD double
#define LL long long
#define ULL unsigned long long
#define min(a,b) ((a<b)?a:b)
#define max(a,b) ((a>b)?a:b)
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
using namespace std;
int const mn=4*1e6+9,mm=8*1e6+9,inf=1e9;LL mo=2333333333333333;
int n,gra,tag,ansp,begin[mn],to[mm],len[mm],num[mm],next[mm];
    LL anss,f[mn][4][2],a[mn][3][2],g[mn],h[mn];
void insert(int u,int v ,int w,int p){
    to[++gra]=v;
    len[gra]=w;
    num[gra]=p;
    next[gra]=begin[u];
    begin[u]=gra;
}
void add(int now,int pos,LL far){
    fd(i,3,1){
        if(f[now][i][0]>far)break;
        f[now][i+1][0]=f[now][i][0];
        f[now][i+1][1]=f[now][i][1];
        f[now][i][0]=far;
        f[now][i][1]=pos;
    }
}
void dfs(int now,int pre){
    fr(i,now)if(to[i]!=pre){
        dfs(to[i],now);
        add(now,to[i],f[to[i]][1][0]+len[i]);
        h[now]=max(h[now],h[to[i]]);
    }
    h[now]=max(h[now],f[now][1][0]+f[now][2][0]);
}
LL get(int now,int tag){
    fo(i,1,3)if(f[now][i][1]!=tag)return f[now][i][0];
}
LL get2(int now,int tag){
    if(f[now][1][1]==tag)return f[now][2][0]+f[now][3][0];
    else if(f[now][2][1]==tag)return f[now][1][0]+f[now][3][0];
    else return f[now][1][0]+f[now][2][0];
}
void addh(int now,int pos,LL far){
    fd(i,2,1){
        if(a[now][i][0]>far)break;
        a[now][i+1][0]=a[now][i][0];
        a[now][i+1][1]=a[now][i][1];
        a[now][i][0]=far;
        a[now][i][1]=pos;
    }
}
LL geth(int now,int tag){
    fo(i,1,2)if(a[now][i][1]!=tag)return a[now][i][0];
}
void dfs2(int now,int pre,LL tmx){
    fr(i,now)if(to[i]!=pre)
        addh(now,to[i],h[to[i]]);
    fr(i,now)if(to[i]!=pre){
        LL tmp=get(now,to[i]);
        g[to[i]]=max(tmp,g[now])+len[i];
        tmp=h[to[i]];LL tmp2=get2(now,to[i]),tmp3=get(now,to[i])+g[now];
        tmp2=max(tmp2,tmp3);tmp3=geth(now,to[i]);
        tmp2=max(tmp2,tmp3);
        tmp2=max(tmp2,tmx);
        tmp3=max(tmp,tmp2);LL tmp4=min(tmp,tmp2);
        anss=(anss+tmp3*23333+tmp4*2333+1ll*233*num[i]*num[i]+23*num[i]+2)%mo;
        dfs2(to[i],now,tmp2);
    }
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d",&n);int u,v,w;
    fo(i,1,n-1){
        scanf("%d%d%d",&u,&v,&w);
        insert(u,v,w,i);insert(v,u,w,i);
    }
    dfs(1,0);
    dfs2(1,0,0);
    printf("%lld",anss);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值