bzoj3924 [Zjoi2015]幻想乡战略游戏

动态点分治的题好恶心啊

思路倒是比较简单,点分树上每个节点维护子树到他的权值和,到他父亲的权值和,以及点权和。

发现查询的是带权重心,于是每次查到一个点,就在他原树的儿子中找一个比他优的,走到那边的点分树上的儿子,没有比他优的话答案就是他。

一开始一直以为知道父亲的答案,儿子的答案可以$log$转移,后来发现不行,但是那样复杂度就变成了$O(nlg^3n)$,卡不过去,所以树上查询距离的话必须要欧拉序O(1)查询。

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<iostream>
  4 #include<algorithm>
  5 #include<cmath>
  6 #include<vector>
  7 #define N 100500
  8 #define int long long
  9 using namespace std;
 10 int val[N];
 11 int e=1,head[N];
 12 struct edge{
 13     int v,w,next;
 14 }ed[N<<1];
 15 void add(int u,int v,int w){
 16     ed[e].v=v;ed[e].w=w;
 17     ed[e].next=head[u];
 18     head[u]=e++;
 19 }
 20 int a[2*N],L[N],R[N],cnt,minn[2*N][20],lg[2*N];
 21 void dfs(int x,int fa){
 22     a[++cnt]=val[x];L[x]=cnt;
 23     for(int i=head[x];i;i=ed[i].next){
 24         int v=ed[i].v;
 25         if(v==fa)continue;
 26         val[v]=val[x]+ed[i].w;
 27         dfs(v,x);a[++cnt]=val[x];
 28     }
 29     if(a[cnt]!=val[x])a[++cnt]=val[x];
 30     R[x]=cnt;
 31 }
 32 int dis(int x,int y){
 33     int l=R[x],r=L[y];
 34     if(l>r)swap(l,r);
 35     return val[x]+val[y]-2*min(minn[l][lg[r-l+1]],minn[r-(1<<lg[r-l+1])+1][lg[r-l+1]]);
 36 }
 37 int size[N],maxn[N];
 38 bool vis[N];
 39 int root,sum,rt,n,m,tot;
 40 int f[N];
 41 vector<pair<int,int> >son[N];
 42 void getroot(int x,int fa){
 43     size[x]=1;maxn[x]=0;
 44     for(int i=head[x];i;i=ed[i].next){
 45         int v=ed[i].v;
 46         if(v==fa||vis[v])continue;
 47         getroot(v,x);
 48         size[x]+=size[v];
 49         maxn[x]=max(maxn[x],size[v]);
 50     }
 51     maxn[x]=max(maxn[x],sum-size[x]);
 52     if(maxn[x]<maxn[root])root=x;
 53 }
 54 void init(int x){
 55     vis[x]=1;int all=sum;
 56     for(int i=head[x];i;i=ed[i].next){
 57         int v=ed[i].v;
 58         if(vis[v])continue;
 59         root=0;sum=size[v]<size[x]?size[v]:all-size[x];
 60         getroot(v,0);
 61         son[x].push_back(make_pair(v,root));
 62         f[root]=x;
 63         init(root);
 64     }
 65 }
 66 int sum1[N],sum2[N],num[N];
 67 void update(int x,int y){
 68     tot+=y;
 69     int now=x;
 70     while(now){
 71         num[now]+=y;
 72         sum1[now]+=y*dis(x,now);
 73         if(f[now])sum2[now]+=y*dis(x,f[now]);
 74         now=f[now];
 75     }
 76 }
 77 int query(int x){
 78     int ans=sum1[x],now=x;
 79     while(1){
 80         ans+=(sum1[f[now]]-sum2[now])+(num[f[now]]-num[now])*dis(x,f[now]);
 81         now=f[now];if(!f[now])break;
 82     }
 83     return ans;
 84 }
 85 int query(){
 86     int now=rt,last,val=sum1[rt];
 87     while(1){
 88         last=now;
 89         for(int i=0;i<son[now].size();i++){
 90             int v=son[now][i].first,nxt=son[now][i].second;
 91             int w=query(v);
 92             if(w<val){now=nxt;val=query(now);break;}
 93         }
 94         if(now==last)return val;
 95     }
 96 }
 97 void rmq(){
 98     for(int i=1,j=1,tim=0;(i>>1)<=cnt;i<<=1,tim++)
 99         while(j<=cnt&&j<(i<<1))lg[j++]=tim;
100     for(int i=1;i<=cnt;i++)minn[i][0]=a[i];
101     for(int i=1;(1<<i)<=cnt;i++)
102         for(int j=1;j+(1<<i)-1<=cnt;j++)
103             minn[j][i]=min(minn[j][i-1],minn[j+(1<<i-1)][i-1]);
104 }
105 signed main(){
106     scanf("%lld%lld",&n,&m);
107     for(int i=1,u,v,w;i<n;i++){
108         scanf("%lld%lld%lld",&u,&v,&w);
109         add(u,v,w);add(v,u,w);
110     }
111     dfs(1,1);rmq();
112     root=0;maxn[0]=N;sum=n;
113     getroot(1,0);
114     rt=root;
115     init(root);
116     int x,y;
117     while(m--){
118         scanf("%lld%lld",&x,&y);
119         update(x,y);
120         printf("%lld\n",query());
121     }
122     return 0;
123 }
View Code

 

posted @ 2018-01-08 18:04  Ren_Ivan  阅读(293)  评论(0编辑  收藏  举报