BZOJ3302: [Shoi2005]树的双中心

Posted Blue233333

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ3302: [Shoi2005]树的双中心相关的知识,希望对你有一定的参考价值。

n<=50000的树,深度<=100,有点权,选两个点x,y,使最小。

dis取了min之后,整个树就会以某条边为分界线分成两半,一半归一个点管。如果是两棵完全独立的树的话,那肯定分别取这两棵树的带权重心。但割掉某条边再找两边重心,这种情况不一定是合法情况。例如:

上图中,虚线边被断开,两边的重心分别是星标节点。这不是一个合法方案,但它显然不如一个合法方案的答案优:

所以放心大胆地割就好了。注意到本题中树的深度h很小,所以割边后涉及的子树信息修改操作都可以暴力修改。

把树以某点为根,希望能预处理出一些信息使得能够在O(h)的时间内找到割完边每一半的重心。如果是普通的一棵树,知道了子树大小(权值和)之后,从某点出发最多走2h步之后就可以到重心。割了某条边之后,一个点最多只会有一棵子树信息被修改。因此记最大儿子和次大儿子即可。

至于答案的记录我写的有点丑。如果想的话可以看一下我怎么写的,不然就直接看怎么写更方便吧。所有子树大小加起来即为根节点为重心的总代价,因为这样加起来,越是下面的点加的次数越多。断掉某条边之后,修改子树大小的同时可修改根节点为重心答案,然后从根节点开始向重儿子走,只要比较最大儿子和次大儿子子树权和即可,边走边改答案。走一步,就是走到的这棵子树权值和少算一次,其外面的权值和多算一次。

我是记了每个节点为根的答案。。式子有点乱。。

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 #include<algorithm>
 5 //#include<queue>
 6 //#include<iostream>
 7 using namespace std;
 8  
 9 int t,n;
10 #define maxn 50011
11 struct Edge{int to,next;}edge[maxn<<1];int first[maxn],le,val[maxn];
12 void in(int x,int y) {Edge &e=edge[le];e.to=y;e.next=first[x];first[x]=le++;}
13 void insert(int x,int y) {in(x,y);in(y,x);}
14 void init()
15 {
16     scanf("%d",&n);
17     memset(first,0,sizeof(first));le=2;
18     int x,y;
19     for (int i=1;i<n;i++)
20     {
21         scanf("%d%d",&x,&y);
22         insert(x,y);
23     }
24     for (int i=1;i<=n;i++) scanf("%d",&val[i]);
25 }
26 #define LL long long
27 int size[maxn],dep[maxn],f[maxn],tot,hea[maxn],h2[maxn];LL aa[maxn];
28 void dfs(int x,int fa)
29 {
30     size[x]=val[x];dep[x]=dep[fa]+1;f[x]=fa;tot+=val[x];
31     hea[x]=h2[x]=0;aa[x]=0;
32     for (int i=first[x];i;i=edge[i].next)
33     {
34         const Edge &e=edge[i];if (e.to==fa) continue;
35         dfs(e.to,x);size[x]+=size[e.to];
36         aa[x]+=aa[e.to]+size[e.to];
37         if (size[e.to]>size[hea[x]]) h2[x]=hea[x],hea[x]=e.to;
38         else if (size[e.to]>size[h2[x]]) h2[x]=e.to;
39     }
40 }
41 int main()
42 {
43         init();
44         tot=0;dfs(1,0);
45         LL ANS=1e15;
46         for (int i=2;i<le;i+=2)
47         {
48             int x=edge[i].to,y=edge[i^1].to;
49             if (dep[x]<dep[y]) {int t=x;x=y;y=t;}
50             for (int i=f[x],cnt=1;i;i=f[i],cnt++) size[i]-=size[x],aa[i]-=aa[x]+1ll*cnt*size[x];
51             tot-=size[x];
52             LL ans=0,qq=0;
53             for (int now=1;;)
54             {
55                 int tmp=(hea[now]==x?(h2[now]?size[h2[now]]:0):(h2[now]==x?size[hea[now]]:
56                 max(size[hea[now]],size[h2[now]])))*2;
57                  
58                 if (tmp<=tot) {ans+=aa[now]+qq;break;}
59                 if (size[hea[now]]>size[h2[now]] && hea[now]!=x) now=hea[now],
60                 qq+=aa[f[now]]-(aa[now]+size[now])+tot-size[now];
61                 else now=h2[now],qq+=aa[f[now]]-(aa[now]+size[now])+tot-size[now];
62             }
63             qq=0;
64             for (int now=x;;)
65             {
66                 if (size[hea[now]]*2<=size[x]) {ans+=aa[now]+qq;break;}
67                 now=hea[now],qq+=aa[f[now]]-(aa[now]+size[now])+size[x]-size[now];
68             }
69             ANS=min(ANS,ans);
70             tot+=size[x];
71             for (int i=f[x],cnt=1;i;i=f[i],cnt++) size[i]+=size[x],aa[i]+=aa[x]+1ll*cnt*size[x];
72         }
73         printf("%lld\\n",ANS);
74     return 0;
75 }
View Code

 

以上是关于BZOJ3302: [Shoi2005]树的双中心的主要内容,如果未能解决你的问题,请参考以下文章

题解-SHOI2005 树的双中心

题解-SHOI2005 树的双中心

P2726 [SHOI2005]树的双中心 题解

BZOJ 2103/3302/2447 消防站 树的重心DFSTreeDP

bzoj3304[Shoi2005]带限制的最长公共子序列 DP

bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic