2019江西省赛A题 HDU 6567 Cotree 树形dp

题意:给定n的点,n-2条边,也就是给出了两棵树,要求加一条边连接两棵树并使得连接好的这棵树上任意两点距离和最小。

思路:先取任意一点bfs来分开两棵树,此时假设第一棵树有n1个点,第二棵树有n2个点,分别对这两棵树进行树形dp,dpsum[i]记录在本棵树上其余点到点i的距离和。分别找出两棵树上的最小的dpsum值,此时连接这两个点就是最优解。此时要统计任意点距离和了:

第一种比较暴力的做法,就是直接对这两个点加边,然后跑树上任意两点距离和的板子。

第二种做法我们可以采取算贡献的方式:对于这个两个树,树上任意两点距离和就是每棵树上(dpsum求和/2),再考虑连接两个点的这条边的贡献就是n1*n2因为第一棵树的每个点都要和第二棵树上的每个点算一次距离,然后就是第一棵树上的点在连接第二棵树的时候要在第二棵树上走n1次第二棵树最大的dpsum值,同理第二棵树上的点在连接第一棵树的时候要在第一棵树上走n2次第二棵树最大的dpsum值。因此最终的答案就是ans=ans1+ans2+dpsum[dian1]*n2+dpsum[dian2]*n1+n1*n2;

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 const int N=2e5+5;
  5 int n,m,t,head[N],sumson[N],tot,vis[N],ji;
  6 ll dpfa[N],dpson[N],dpsum[N];
  7 struct p{
  8     int next,to;
  9 }edge[N];
 10 void add(int u,int v){
 11     edge[tot]={head[u],v};
 12     head[u]=tot++;
 13 }
 14 void bfs(){
 15     queue<int>Q;
 16     vis[1]=1;
 17     Q.push(1);
 18     while(!Q.empty()){
 19         int x=Q.front();
 20         Q.pop();
 21         for(int i=head[x];i!=-1;i=edge[i].next){
 22             int to=edge[i].to;
 23             if(vis[to])continue;
 24             vis[to]=1;
 25             Q.push(to);
 26         }
 27     }
 28 }
 29 void dfs(int x,int fa){
 30     sumson[x]=1;
 31     dpson[x]=0;
 32     for(int i=head[x];i!=-1;i=edge[i].next){
 33         int to=edge[i].to;
 34         if(to==fa)continue;
 35         dfs(to,x);
 36         sumson[x]+=sumson[to];
 37         dpson[x]+=dpson[to]+sumson[to];
 38     }
 39 }
 40 void dfs2(int x,int fa,int nn){
 41     if(x!=1&&x!=ji){
 42         dpfa[x]=dpsum[fa]-dpson[x]-2*sumson[x]+nn;
 43         dpsum[x]=dpfa[x]+dpson[x];
 44     }
 45     for(int i=head[x];i!=-1;i=edge[i].next){
 46         int to=edge[i].to;
 47         if(to==fa)continue;
 48         dfs2(to,x,nn);
 49     }
 50 }
 51 int main(){
 52     while(~scanf("%d",&n)){
 53         if(n==2){
 54             printf("1\n");
 55             continue;
 56         }
 57         for(int i=0;i<=n+3;i++){
 58             dpson[i]=dpsum[i]=dpfa[i]=sumson[i]=vis[i]=0;
 59             head[i]=-1;
 60         }
 61         int u,v,n1=0,n2;tot=ji=1;
 62         for(int i=1;i<=n-2;i++){
 63             scanf("%d%d",&u,&v);
 64             add(u,v);
 65             add(v,u);
 66         }
 67         bfs();
 68         for(int i=1;i<=n;i++)n1+=vis[i];
 69         n2=n-n1;
 70         dfs(1,0);
 71         dpsum[1]=dpson[1];
 72         dfs2(1,0,n1);
 73         ll maxx=1ll<<60,ans1=0,ans2=0,ans=0;
 74         int dian1,dian2;
 75         for(int i=1;i<=n;i++){
 76             ans1+=dpsum[i];
 77             if(dpsum[i]&&dpsum[i]<maxx){
 78                 dian1=i;
 79                 maxx=dpsum[i];
 80             }
 81         }
 82         for(int i=1;i<=n;i++){
 83             if(!vis[i]){
 84                 ji=i;
 85                 break;
 86             }
 87         }
 88         dfs(ji,0);
 89         dpsum[ji]=dpson[ji];
 90         dfs2(ji,0,n2);
 91         maxx=1ll<<60;
 92         for(int i=1;i<=n;i++){
 93             if(!vis[i]&&dpsum[i]<maxx){
 94                 dian2=i;
 95                 maxx=dpsum[i];
 96             }
 97         }
 98         for(int i=1;i<=n;i++)ans+=dpsum[i];
 99         ans2=ans-ans1;
100         ans1/=2,ans2/=2,ans/=2;
101         ans=ans+dpsum[dian1]*n2+dpsum[dian2]*n1+n1*n2;
102         printf("%lld\n",ans);
103     }
104     return 0;
105 }

 

posted @ 2019-07-29 22:50  hrbust_nzc  阅读(348)  评论(0编辑  收藏  举报