[CF391E2]Three Trees

https://zybuluo.com/ysner/note/1246822

题面

有三棵树,建两条边让他们相连,最大化所有点对距离之和。

  • \(40pts\ n\leq1000\)
  • \(100pts\ n\leq10^5\)

解析

\(40pts\)算法

似乎是怎么暴力怎么搞。
先预处理出三棵树内所有点对的距离\(dis\)和每一个点到其他点的距离和\(sum\),然后枚举最后另两颗树接到哪棵树上。

接着举例推导两颗树相连时对答案新产生的贡献(考场上推了\(1h\)???)
设建的边两端点分别是\(u\)点,对应\(A\)树,大小为\(size[A]\)
\(v\)点,对应\(B\)树,大小为\(size[B]\)。hree Trees
边长为\(len\)
则贡献为\[sum[u]*size[B]+len*size[A]*size[B]+sum[v]*size[A]\]
(这是整道题的钥匙)

然后枚举另两棵树接在第三棵树的哪个点上,同时计算答案即可。
答案中要同时统计:(设\(A\),\(C\)两树接到\(B\)上)

  • \(A,B,C\)三树各自内部的点对距离和
  • \(A,B\)两树产生的点对距离和
  • \(B,C\)两树产生的点对距离和
  • \(A,C\)两树产生的点对距离和

都是直接套用上面的式子。
复杂度\(O(n^2)\)

\(100pts\)算法

注意到\(O(n^2)\)\(sum\)似乎很傻逼。
可以换种思路,两次\(O(n)\)的树形\(DP\)解决。

第一次统计子树大小\(sz\)和子树所有点到根结点的距离之hree Trees和\(sum\)
\(u\)为父结点,\(v\)为子结点。
\(sz[u]+=sz[v],sum[u]+=sz[v]+sum[v]\)

第二次应用换根思想。
想一想若根变为儿子,原有的\(sum\)会怎么变化。
首先到\(n-sz[v]\)个点的距离会全部\(+1\)
其次,每次往\(v\)移,\(sum\)都需新加上\(u\)结点的其他儿子及子树的贡献(\(u\)子树外的贡献都被\(u\)点统计过了)。
若设父亲\(sum\)加上的值为\(x\)
则儿子\(sum\)加上的值为\(x+n-sz[v]+sum[u]-sum[v]-sz[v]\)

于是预处理复杂度降到\(O(n)\)

接着发现枚举另两棵树接在第三棵树的哪个点上,是复杂度瓶颈。
但是,如设\(B\)\(sum\)值最大的点为\(j\),那么可以猜出接一棵树在上面肯定不会不优(由那个式子决定)。
于是只要枚举另一棵树接在哪个结点上即可。
由于要现算\(len\),复杂度\(O(nlogn)\)

注意下式子运算过程中是否乘爆。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define re register
#define il inline
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define fp(i,a,b) for(re int i=a;i<=b;i++)
#define fq(i,a,b) for(re int i=a;i>=b;i--)
using namespace std;
const int N=2e5+100,inf=1e9;
ll ans;
struct Tree
{
    int h[N],cnt,sz[N],pos,son[N],top[N],f[N];
    ll mx,tot[N],sum[N],dd,d[N],n;
    il Tree(){memset(h,-1,sizeof(h));cnt=0;pos=1;}
    struct Edge{int to,nxt;}e[N<<1];
    il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;e[++cnt]=(Edge){u,h[v]};h[v]=cnt;}
    il void dfs1(re int u,re int fa)
        {
            sz[u]=1;f[u]=fa;d[u]=d[fa]+1;
            for(re int i=h[u];i+1;i=e[i].nxt)
            {
                re int v=e[i].to;
                if(v==fa) continue;
                dfs1(v,u);
                sz[u]+=sz[v];
                if(sz[v]>sz[son[u]]) son[u]=v;
                sum[u]+=sz[v]+sum[v];
            }
        }
    il void dfs2(re int u,re int up)
        {
            top[u]=up;
            if(son[u]) dfs2(son[u],up);
            for(re int i=h[u];i+1;i=e[i].nxt)
            {
                re int v=e[i].to;
                if(v==f[u]||v==son[u]) continue;
                dfs2(v,v);
            }
        }
    il void dfs3(re int u,re int fa,re ll las)
        {
            tot[u]=sum[u]+las;dd+=tot[u];
            if(tot[u]>mx) mx=tot[u],pos=u;
            for(re int i=h[u];i+1;i=e[i].nxt)
            {
                re int v=e[i].to;
                if(v==fa) continue;
                dfs3(v,u,las+n-sz[v]+sum[u]-sum[v]-sz[v]);
            }
        }
    il int LCA(re int u,re int v)
    {
        while(top[u]^top[v])
        {
            if(d[top[u]]<d[top[v]]) swap(u,v);
            u=f[top[u]];
        }
        return d[u]<d[v]?u:v;
    }
    il ll Dis(re int u,re int v){return d[u]+d[v]-2*d[LCA(u,v)];}
}T[4];
il void calc(re int A,re int B,re int C)
{
    re ll s=(T[A].dd+T[B].dd+T[C].dd)+(T[A].mx*T[B].n+T[A].n*T[B].n+T[B].mx*T[A].n)+(T[C].mx*T[B].n+T[B].n*T[C].n+0)+(T[A].mx*T[C].n+0+T[C].mx*T[A].n),ysn=0;
    fp(i,1,T[B].n) ysn=max(ysn,T[B].tot[i]*T[C].n+(T[B].Dis(T[B].pos,i)+2)*T[A].n*T[C].n);
    ans=max(ans,s+ysn);
}
il ll gi()
{
  re ll x=0,t=1;
  re char ch=getchar();
  while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  if(ch=='-') t=-1,ch=getchar();
  while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  return x*t;
}
int main()
{
    T[1].n=gi();T[2].n=gi();T[3].n=gi();
    fp(i,1,T[1].n-1) T[1].add(gi(),gi());
    fp(i,1,T[2].n-1) T[2].add(gi(),gi());
    fp(i,1,T[3].n-1) T[3].add(gi(),gi());
    fp(i,1,3) T[i].dfs1(1,0),T[i].dfs2(1,1),T[i].dfs3(1,0,0),T[i].dd/=2;
    fp(i,1,3)
        fp(j,1,3)
    {
        if(i==j) continue;
        calc(i,j,6-i-j);
    }
  printf("%lld\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/yanshannan/p/9461839.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值