[agc018d]Tree and Hamilton Path

题目大意

给你一颗n个点,边带权c[]的树,请构造一个排列p,求 i=1..ndis(pipi+1) ∑ i = 1.. n d i s ( p i , p i + 1 ) 的最大值。
n≤1e5

解题思路

并不能使用暴力加优化。那么进一步观察性质。
考虑一条边i=(u,v)经过的上界,设s[i]=min(size[v[i]],size[u[i]]),即两边较小的连通分量大小。那么经过次数上界为2*s[i],这条边贡献2*s[i]*c[i]。猜一下结论:是否每条边都能达到次数上界呢?
随便拉一条边i出来,我们间隔地安排两边的点,这条边达到了上界,但很明显,如果两边大小不一样,大的一边的一些点会连在一起,那么大的分量内部不能都达到上界。
如果两边大小一样,我们发现,除了i会少一次,其他边都达到了上界,如果dis(p[n],p[1])也算,那就齐了。所以往重心方向思考。
那么上面说的两个重心的情况,答案就是 2sicic ∑ 2 ∗ s i ∗ c i − c 重 心 。如果是只有一个重心,很显然还是得随便找一条边让他次数-1,才能构造出合法的排列,那么找最小的边即可。

代码

#include<cstdio> 
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
typedef long long ll;
const int N=1e6+5,M=2e6+5,mo=998244353;
int n,x,y,z,i,f[N],siz[N],len[N],mn,ce[5],p;
ll ans;
int tt,fst[N],nxt[N],b[N],c[N];
void cr(int x,int y,int z)
{
    tt++;
    b[tt]=y;
    c[tt]=z;
    nxt[tt]=fst[x];
    fst[x]=tt;
}
void dfs(int x,int y)
{
    siz[x]=1;
    for(int p=fst[x];p;p=nxt[p])
        if (b[p]!=y)
        {
            len[b[p]]=c[p];
            dfs(b[p],x);
            siz[x]+=siz[b[p]];
            cmax(f[x],siz[b[p]]);
        }
}
int main()
{
    freopen("t2.in","r",stdin);
    //freopen("t2.out","w",stdout);
    scanf("%d\n",&n);
    fo(i,1,n-1)
    {
        scanf("%d %d %d",&x,&y,&z);
        cr(x,y,z);
        cr(y,x,z);
    }
    dfs(1,0);
    fo(i,1,n) 
    {
        ans+=1ll*min(siz[i],siz[1]-siz[i])*len[i];
        cmax(f[i],siz[1]-siz[i]);
    }
    ans*=2ll;
    fo(i,1,n) 
        if ((!ce[1])||(f[i]<f[ce[1]]))
        {
            ce[0]=1;ce[1]=i;
        }else if (f[i]==f[ce[1]])
        {
            ce[0]++;ce[2]=i;
        }
    if (ce[0]==2)
    {
        for (p=fst[ce[1]];p;p=nxt[p])
            if (b[p]==ce[2])
                ans-=c[p];
    }else
    {
        mn=1e9;
        for(p=fst[ce[1]];p;p=nxt[p])
            cmin(mn,c[p]);
        ans-=mn;
    }
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值