[ZJOI2015] 幻想乡战略游戏

题解:

个人认为这道题很不友好,因为思路不清晰调了好久。

这道题实际上就是求一棵树的带权重心,很显然一个点越远离带权重心,权值和就会越大。

那么我们先说一下暴力的思路,假设当前点为x,如果存在一个点y,权值和小于x,那么就向y这个方向转移,直到与x相邻的点中没有权值和比x小的,那么此时的x就是答案。

这种做法是$O(n^{2})$的,显然过不了所有的数据,考虑优化。

考虑动态点分,假设我们每次都从根节点x来搜索答案,那么如果当前点不是答案,那么与当前点相邻的点中,一定存在答案比当前点小的点y,而且这个点若存在就是唯一存在。

那么我们找到y所在子树的重心z,继续从z开始做就好。

由于点分树的深度最深为logn,所以这种算法有复杂度保证。

注意:这道题普通的RMQ求LCA过不了,会超时,要用查询复杂度为$O(1)$的欧拉序LCA。

  1 //Never forget why you start
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<cstdlib>
  5 #include<cstring>
  6 #include<cmath>
  7 #include<algorithm>
  8 #define inf (2147483647)
  9 using namespace std;
 10 typedef long long lol;
 11 int n,m,lim;
 12 struct node{
 13   int next,to;
 14   lol dis;
 15 }edge[200005];
 16 int head[100005],size;
 17 void putin(int from,int to,lol dis){
 18   size++;
 19   edge[size].next=head[from];
 20   edge[size].to=to;
 21   edge[size].dis=dis;
 22   head[from]=size;
 23 }
 24 int place[100005],dfscnt,st[200005][21],dfn,o[100005];
 25 lol dis[100005];
 26 void dfs1(int r,int father){
 27   int i,s=++dfn;
 28   st[++dfscnt][0]=s;
 29   o[s]=r;
 30   place[r]=dfscnt;
 31   for(i=head[r];i!=-1;i=edge[i].next){
 32     int y=edge[i].to;
 33     if(y!=father){
 34       dis[y]=dis[r]+edge[i].dis;
 35       dfs1(y,r);
 36       st[++dfscnt][0]=s;
 37     }
 38   }
 39 }
 40 void make(){
 41   lim=log(n*2)/log(2);
 42   for(int i=1;i<=lim;i++)
 43     for(int j=1;j<=dfscnt;j++)
 44       if(j+(1<<i)-1<=dfscnt)
 45     st[j][i]=min(st[j][i-1],st[j+(1<<(i-1))][i-1]);
 46 }
 47 int LCA(int x,int y){
 48   if(place[x]>place[y])swap(x,y);
 49   int lg=log(place[y]-place[x]+1)/log(2);
 50   return o[min(st[place[x]][lg],st[place[y]-(1<<lg)+1][lg])];
 51 }
 52 lol dist(int x,int y){
 53   int lca=LCA(x,y);
 54   return dis[x]+dis[y]-dis[lca]*2;
 55 }
 56 int vis[100005],cnt[100005],d[100005],tot,root,ff[100005];
 57 void getroot(int r,int father){
 58   int i;
 59   cnt[r]=1;d[r]=0;
 60   for(i=head[r];i!=-1;i=edge[i].next){
 61     int y=edge[i].to;
 62     if(!vis[y]&&y!=father){
 63       getroot(y,r);
 64       cnt[r]+=cnt[y];
 65       d[r]=max(d[r],cnt[y]);    
 66     }
 67   }
 68   d[r]=max(d[r],tot-cnt[r]);
 69   if(d[root]>d[r])root=r;
 70 }
 71 void buildtree(int r,int father){
 72   int i,all=tot;
 73   vis[r]=1;ff[r]=father;
 74   for(i=head[r];i!=-1;i=edge[i].next){
 75     int y=edge[i].to;
 76     if(!vis[y]){
 77       if(cnt[y]>cnt[r])cnt[y]=all-cnt[r];tot=cnt[y];
 78       root=0;getroot(y,r);buildtree(root,r);
 79     }
 80   }
 81 }
 82 lol p[100005][2];
 83 void insert(int x,int v){
 84   int i;
 85   for(i=x;ff[i];i=ff[i]){
 86     lol len=dist(x,ff[i]);
 87     p[i][1]+=len*v;
 88     p[ff[i]][0]+=len*v;
 89   }
 90   for(i=x;i;i=ff[i])cnt[i]+=v;
 91 }
 92 lol query(int x){
 93   int i;
 94   lol ans=p[x][0];
 95   for(i=x;ff[i];i=ff[i]){
 96     lol len=dist(x,ff[i]);
 97     ans+=p[ff[i]][0];
 98     ans+=len*(cnt[ff[i]]-cnt[i]);
 99     ans-=p[i][1];
100   }
101   return ans;
102 }
103 lol find(int r,lol ans){
104   int i,to=0;
105   vis[r]=1;
106   for(i=head[r];i!=-1;i=edge[i].next){
107     int y=edge[i].to;
108     lol tmp=query(y);
109     if(tmp<ans){to=y;break;}
110   }
111   if(to){
112     for(i=to;ff[i]!=r;i=ff[i]);
113     ans=query(i);
114     ans=find(i,ans);
115     return ans;
116   }
117   else return ans;
118 }
119 void clean(){
120   memset(head,-1,sizeof(head));
121   size=0;    
122 }
123 int read(){
124   int ans=0,f=1;char i=getchar();
125   while(i<'0'||i>'9'){if(i=='-')f=-1;i=getchar();}
126   while(i>='0'&&i<='9'){ans=ans*10+i-'0';i=getchar();}
127   return ans*f;
128 }
129 int main(){
130   freopen("zjoi15_tree.in","r",stdin);
131   freopen("zjoi15_tree.out","w",stdout);
132   clean();
133   int i,j;
134   n=read();m=read();
135   for(i=1;i<n;i++){
136     int u,v;lol l;
137     u=read();v=read();l=read();
138     putin(u,v,l);
139     putin(v,u,l);    
140   }
141   dfs1(1,0);make();
142   tot=n;root=0;d[0]=inf;
143   getroot(1,0);buildtree(root,0);
144   for(i=1;i<=n;i++)if(!ff[i]){root=i;break;}
145   memset(cnt,0,sizeof(cnt));
146   while(m--){
147     int x,v;
148     x=read();v=read();
149     insert(x,v);
150     printf("%lld\n",find(root,p[root][0]));    
151   }
152   return 0;    
153 }

 

posted @ 2018-01-22 09:04  kakakakakaka  阅读(212)  评论(0编辑  收藏  举报

Never forget why you start

//鼠标爆炸特效