【BZOJ 3924】[Zjoi2015]幻想乡战略游戏

题目:

  

题解:

  对点分树理解加深了233,膜拜zzh干翻紫荆花。

  感谢zzh的讲解。

  首先优化基于传统DP,假设树不发生变化,我们就可以利用DP求出带权重心。

  考虑修改,我们思路不变,还是从root开始找,但发现这样会被卡成$n^2$,原因是每次经过点太多,为了优化,考虑点分树,由于点分树的性质使得假设我们可以在点分树上找到最优解,那么每次最多经过$log$个节点,可以保证时间复杂度。

  然后考虑在点分树转移,假设当前节点为x,我们枚举其在原树中的边,假设当前枚举边的另一端为y,那么由DP可以得出如果以当前边分为两半,若y的一半点权和大于所有点权的一半,那么最优解一定在y那边存在,然后我们由点分树直接跳跃到y对应的块中。若不存在这样的y,则x一定为最优解。

  这样的话我们的目的就是求x点对应的答案以及y一边对应的点权和,我们用三个数组来记录当前x的点分子树的点权和,点分子树到达x的$d*dis$和,以及到达其父亲的$d*dis$和,这样统计x的答案就可以在$log$的时间内完成。

  对于y我们可以开一个$log$大小的数组来记录在点分数上走过的点,并按照原树dfs排序,每次到达一个新的x用$log$更新,并在此序列上维护一个$sum$表示经过路径上x与其儿子s点权和之差,那么考虑若枚举的y是x在原树的儿子或父亲时的情况,分类讨论,利用$sum$快速求出y一边的点权和。

  综上所述,时间复杂度为$O(20nlog_2^nlog_2^{log_2^n})$

代码:

  1 #define Troy 
  2 #define inf 0x7fffffff
  3 
  4 #include "bits/stdc++.h"
  5 
  6 using namespace std;
  7 
  8 inline int read(){
  9     int s=0,k=1;char ch=getchar();
 10     while (ch<'0'|ch>'9')    ch=='-'?k=-1:0,ch=getchar();
 11     while (ch>47&ch<='9')    s=s*10+(ch^48),ch=getchar();
 12     return s*k;
 13 }
 14 
 15 const int N=1e5+5;
 16 
 17 typedef long long ll;
 18 
 19 struct edges {
 20     int v,nv,w;edges *last;
 21 }edge[N<<1],*head[N];int cnt;
 22 
 23 inline void push(int u,int v,int w){
 24     edge[++cnt]=(edges){v,0,w,head[u]};head[u]=edge+cnt;
 25 }
 26 
 27 int bit[30];
 28 
 29 class ST{
 30 public:
 31     inline void build(int *a,int n){
 32         lgs[0]=-1;
 33         register int i,j;
 34         for (i=1;i<=n;++i)  lgs[i]=lgs[i>>1]+1,f[i][0]=a[i];
 35         for (i=1;bit[i]<=n;++i)
 36             for (j=1;j+bit[i]<=n+1;++j)
 37                 f[j][i]=min(f[j][i-1],f[j+bit[i-1]][i-1]);
 38     }
 39 
 40     inline int query(int l,int r){
 41         if(r<l) swap(l,r);int t=lgs[r-l+1];
 42         return min(f[l][t],f[r-bit[t]+1][t]);
 43     }   
 44 private:
 45     int f[N<<1][20],lgs[N<<1];
 46 }RMQ;
 47 
 48 int dis[N],eular[N<<1],num,beg[N],End[N],n,m;
 49 
 50 inline void DFS(int x,int fa){
 51     eular[beg[x]=++num]=dis[x];
 52     for(edges *i=head[x];i;i=i->last)   if(i->v^fa){
 53         dis[i->v]=dis[x]+i->w;
 54         DFS(i->v,x);
 55         eular[++num]=dis[x];
 56     }End[x]=num;
 57 }
 58 
 59 inline ll get_dis(int x,int y){
 60     return dis[x]+dis[y]-(RMQ.query(beg[x],beg[y])<<1);
 61 }
 62 
 63 class Point_Divide_Tree{
 64 public:
 65     int root,tot,fat[N],size[N],heavy[N],dsum[N];
 66     bool vis[N];
 67     ll dissum[N],fdissum[N];
 68 
 69     inline void dfs(int x,int fa){
 70         size[x]=1,heavy[x]=0;
 71         for(edges *i=head[x];i;i=i->last)   if(i->v!=fa&&!vis[i->v]){
 72             dfs(i->v,x),size[x]+=size[i->v];
 73             heavy[x]=max(heavy[x],size[i->v]);
 74         }heavy[x]=max(heavy[x],tot-size[x]);
 75         if(heavy[root]>heavy[x])    root=x;
 76     }
 77 
 78     inline void build(int x,int fa){
 79         root=0,dfs(x,0);
 80         vis[x=root]=true,fat[x]=fa,dfs(x,x);
 81         for (edges *i=head[x];i;i=i->last) if(!vis[i->v]){
 82             tot=size[i->v];
 83             build(i->v,x),i->nv=root;
 84         }root=x;
 85     }
 86 
 87     inline void insert(int x,int y,int val){
 88         tot+=val;
 89         while(x){
 90             dsum[x]+=val;
 91             dissum[x]+=get_dis(x,y)*val;
 92             if(fat[x]) 
 93                 fdissum[x]+=get_dis(fat[x],y)*val;            
 94             x=fat[x];
 95         }
 96     }
 97 
 98     ll ans,sum[30];
 99     int pos[30],leth;
100 
101     inline int calc(int fa,int x,int real){
102         int ret=dsum[real];
103         if(dis[x]<dis[fa]){
104             int l=lower_bound(pos+1,pos+leth+1,beg[fa])-pos,
105                 r=upper_bound(pos+1,pos+leth+1,End[fa])-pos-1;
106             ret+=sum[leth]-sum[r]+sum[l-1];
107         }else{
108             int l=lower_bound(pos+1,pos+leth+1,beg[x])-pos,
109                 r=upper_bound(pos+1,pos+leth+1,End[x])-pos-1;
110             ret+=sum[r]-sum[l-1];
111         }
112         return ret;
113     }
114 
115     inline ll calc(int x){        
116         ll ret=dissum[x];
117         int p=x;
118         while(fat[x]){
119             ret+=(dsum[fat[x]]-dsum[x])*get_dis(fat[x],p)+dissum[fat[x]]-fdissum[x];
120             x=fat[x];
121         }return ret;
122     }
123 
124     inline void update(int x,int y){
125         ll now=dsum[x]-dsum[y];
126         for(int i=leth+1;i;--i){
127             sum[i]=sum[i-1]+now;
128             if(i==1||pos[i-1]<=beg[x]){
129                 pos[i]=beg[x];
130                 break;
131             }else   pos[i]=pos[i-1];
132         }++leth;
133     }
134 
135     inline void query(int x){
136         for(edges *i=head[x];i;i=i->last) if(i->nv){
137             if(calc(x,i->v,i->nv)*2>=tot){
138                 update(x,i->nv);
139                 ans=calc(i->nv);               
140                 query(i->nv);
141                 break;
142             }
143         }
144     }
145 
146     inline void query(){
147         leth=0;
148         ans=dissum[root];
149         query(root);
150         printf("%lld\n",ans);
151     }
152 }tree;
153 
154 int main(){
155     register int i,j;
156     for (i=0;i<=20;++i)  bit[i]=1<<i;
157     n=read(),m=read();
158     for (i=1;i^n;++i){
159         int a=read(),b=read(),c=read();
160         push(a,b,c),push(b,a,c);
161     }
162     DFS(1,1),RMQ.build(eular,num);    
163     tree.tot=n,tree.heavy[0]=inf;
164     tree.build(1,0),tree.tot=0;
165     while(m--){
166         i=read(),j=read();
167         tree.insert(i,i,j);
168         tree.query();
169     }
170 }

 

posted @ 2017-12-08 19:10  Troywar  阅读(781)  评论(0编辑  收藏