BZOJ 3924[Zjoi2015]幻想乡战略游戏
Posted Troy Ricardo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 }
以上是关于BZOJ 3924[Zjoi2015]幻想乡战略游戏的主要内容,如果未能解决你的问题,请参考以下文章
[ZJOI2015][bzoj3924] 幻想乡战略游戏 [动态点分治]
bzoj3924[Zjoi2015]幻想乡战略游戏 动态树分治