1. 程式人生 > >【BZOJ 3924】[Zjoi2015]幻想鄉戰略遊戲

【BZOJ 3924】[Zjoi2015]幻想鄉戰略遊戲

upd += num ffffff esp image 優化 color 代碼

題目:

  技術分享圖片

題解:

  對點分樹理解加深了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]幻想鄉戰略遊戲