10.20T3 DLZ常数剪枝+枚举边+带权重心
Description
DLZ常数史称杜林哲常数,用来优化树中最值以及枚举边的问题
DLZ常数的计算方法是看树的规模,再进行一系列的运算可以得到
在这道题里面DLZ常数可以控制在11~20之间,就可以剪枝了
做法:
枚举每一条边,找两边子树的带权重心,此时必定最优,然后计算出相应的值然后进行比较
DLZ剪枝定律:当两边子树的比值为一个DLZ常数范围的时候,剪枝必定成立并且可以优化时间复杂度
这个定律在bashu联考的时候被DLZ提出
复杂度O(n^2/DLZ)
code:
1 #include<iostream> 2 #include<cstdio> 3 #define N 100005 4 using namespace std; 5 struct node{ 6 int u,v; 7 }e[N]; 8 int first[N],nxt[N],cnt; 9 void add(int u,int v){ 10 e[++cnt].u=u; 11 e[cnt].v=v; 12 nxt[cnt]=first[u]; 13 first[u]=cnt; 14 } 15 int siz[N],root,a[N],mxsiz,mark; 16 int getroot(int x,int father){ 17 siz[x]=a[x]; 18 int max0=0; 19 for(int i=first[x];i;i=nxt[i]){ 20 int v=e[i].v; 21 if(v==father)continue; 22 getroot(v,x); 23 siz[x]+=siz[v]; 24 max0=max(max0,siz[v]); 25 } 26 max0=max(max0,mxsiz-siz[x]); 27 if(max0<mark){ 28 root=x; 29 mark=max0; 30 } 31 } 32 int temp,dis[N],End; 33 void getdis(int x,int father){ 34 temp+=dis[x]*a[x]; 35 for(int i=first[x];i;i=nxt[i]){ 36 int v=e[i].v; 37 if(v==father)continue; 38 if(v==End)continue; 39 dis[v]=dis[x]+1; 40 getdis(v,x); 41 } 42 } 43 int s[N],suma,ans=0x3f3f3f3f; 44 void cal(int x,int y){ 45 // cout<<"left->"<<x<<" right->"<<y<<endl; 46 temp=0; 47 mxsiz=s[x],mark=mxsiz; 48 // cout<<"mxsiz->"<<mxsiz<<endl; 49 getroot(x,y),End=y; 50 dis[root]=0; 51 getdis(root,0); 52 mxsiz=suma-s[x],mark=mxsiz; 53 getroot(y,x),End=x; 54 dis[root]=0; 55 getdis(root,0); 56 ans=min(temp,ans); 57 } 58 void work(int x,int father){ 59 s[x]=a[x]; 60 for(int i=first[x];i;i=nxt[i]){ 61 int v=e[i].v; 62 if(v==father)continue; 63 work(v,x); 64 s[x]+=s[v]; 65 if(s[v]*20<suma)continue; 66 cal(v,x); 67 } 68 } 69 int main(){ 70 int n; 71 cin>>n; 72 for(int i=1;i<n;i++){ 73 int u,v; 74 cin>>u>>v; 75 add(u,v); 76 add(v,u); 77 } 78 for(int i=1;i<=n;i++){ 79 cin>>a[i]; 80 suma+=a[i]; 81 } 82 work(1,0); 83 cout<<ans; 84 return 0; 85 }
over