【GDSOI2017模拟】奶酪

5 篇文章 0 订阅
3 篇文章 0 订阅

Description

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

Solution

比较经典的套路题,很明显可以用树形DP来解决,首先要预处理一个点向下的链长最大值、次大值、第三大值,还有以这个点为根的子树内的链长最大值,切掉这个点与它父亲相连的边后除他以外的:最长链长、以他父亲为起点的链的最长链长。然后就可以很想当然地转移了。注意一些特殊情况,比如说一条链的长度不能够对某一段重复计算;切掉与父亲连边的除自己子树以外的最长链的答案,有可能是某一棵子树的最长链长。细节还是有一点的,慢慢思考一下,这里贴个标给大家参考一下。

Code

#include<iostream>
#include<string.h>
#include<math.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define rep(i,x) for(i=la[x];i;i=ne[i])
typedef long long ll;
const int N=4e6+3;ll MO=2333333333333333;
struct arr{
    ll mx1,mx2,mx3,son,fa,an;
}f[N];
int la[N],ne[N*2],da[N*2],len[N*2],fl[N],d[N],U[N],V[N],p[N];
int n,i,j,sum,num,x,y,z,l,r;
ll tot1,tot2,ans,Mx1[N],Mx2[N],t;
char c;
int read(){
    for(c=getchar();c<'0'||c>'9';c=getchar());
    int x=c-48;
    for(c=getchar();c>='0'&&c<='9';c=getchar()) x=x*10+c-48;
    return x;
}
void ins(int x,int y,int z){
    da[++sum]=y,ne[sum]=la[x],la[x]=sum,len[sum]=z;
    da[++sum]=x,ne[sum]=la[y],la[y]=sum,len[sum]=z;
}
void bfs(){
    int i;
    l=0;r=d[1]=p[1]=1;
    while(l<r){
        x=d[++l];
        rep(i,x) if(!p[da[i]]) 
            d[++r]=da[i],f[da[i]].fa=f[x].fa+fl[x],p[da[i]]=p[x]+1,fl[da[i]]=len[i];
    }
}
int main(){
    freopen("cheese.in","r",stdin);
    freopen("cheese.out","w",stdout);

    n=read();
    fo(i,1,n-1){
        x=read(),y=read(),z=read();
        ins(x,y,z),U[i]=x,V[i]=y;
    }
    bfs();
    fd(i,n,1){
        x=d[i];
        rep(j,x) if(p[da[j]]>p[x]){
            t=f[da[j]].mx1+len[j];
            if(t>=f[x].mx1) f[x].mx3=f[x].mx2,f[x].mx2=f[x].mx1,f[x].mx1=t;
            else if(t>=f[x].mx2) f[x].mx3=f[x].mx2,f[x].mx2=t;
            else if(t>f[x].mx3) f[x].mx3=t;

            //f[x].son=max(f[x].son,f[da[j]].son);
            f[x].son=(f[x].son<f[da[j]].son)?f[da[j]].son:f[x].son;
        }
        //f[x].son=max(f[x].son,f[x].mx1+f[x].mx2);
        f[x].son=(f[x].son<f[x].mx1+f[x].mx2)?f[x].mx1+f[x].mx2:f[x].son;
        if(Mx1[p[x]]<=f[x].son) Mx2[p[x]]=Mx1[p[x]],Mx1[p[x]]=f[x].son;
        else if(Mx2[p[x]]<f[x].son) Mx2[p[x]]=f[x].son;
    }
    fo(i,1,n){
        x=d[i];
        rep(j,x) if(p[da[j]]>p[x]){         
            if(f[x].mx1==f[da[j]].mx1+len[j]) 
                f[da[j]].fa=(f[x].fa+fl[x]>f[x].mx2)?f[x].fa+fl[x]:f[x].mx2;
            else f[da[j]].fa=(f[x].fa+fl[x]>f[x].mx1)?f[x].fa+fl[x]:f[x].mx1;

            if(f[x].mx1==f[da[j]].mx1+len[j]) 
                f[da[j]].an=f[x].mx2+max(f[x].mx3,f[x].fa+fl[x]);
            else if(f[x].mx2==f[da[j]].mx1+len[j]) 
                f[da[j]].an=f[x].mx1+max(f[x].mx3,f[x].fa+fl[x]);
            else f[da[j]].an=f[x].mx1+max(f[x].mx2,f[x].fa+fl[x]);

            if(Mx1[p[da[j]]]==f[da[j]].son) f[da[j]].an=max(f[da[j]].an,Mx2[p[da[j]]]);
            else f[da[j]].an=max(f[da[j]].an,Mx1[p[da[j]]]);

            f[da[j]].an=max(f[da[j]].an,f[x].an);
        }
    }
    fo(i,1,n-1){
        if(p[U[i]]>p[V[i]]) swap(U[i],V[i]);
        tot1=max(f[V[i]].an,f[V[i]].son);
        tot2=min(f[V[i]].an,f[V[i]].son);
        t=(tot1*23333%MO+tot2*2333%MO+233*i%MO*i%MO+23*i+2)%MO;
        ans=(ans+t)%MO;
    }
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值