<题解>幻想乡战略游戏
作者:互联网
看到题面,很容易就想到,这是要你找树上的重心,只不过这个重心是在带边权的树上
所以对于这个我们在树上找这个重心
一开始我想的是,我要更新权值,然后把每个点的答案更新一下
就取最大值,这好像是O(....),我也不会算这个复杂度,好像太大了
后来去学习了一下,点分树;;
知道了有点分树这个东西,立刻就想到
如果我们在点分树上寻找这个带权重心,是不是就可以保证复杂度了???
我先去打了板子,就是上一篇博客,一开始我就想用树状数组
然后我发现自己狭隘了,用树状数组根本维护不了每个点的军队数量
然后我就想别的办法,想不出来,颓一发题解,可是刚看到题解,他竟然没用树状数组,而是维护了两个数组,一个存答案,一个存数量
和树状数组的思想类似,还是要维护两个值,维护自己的,维护爹的,不过这里还有另外一个套路
ans加上爹的,减去自己的,还要加上x到爹的价值
然后还有一道题也是这样维护,没啥意思,不过那个题调了一晚上加上一早起(MLE+TLE),为啥呢? 手残打错一个变量;
所以这个题就这样做,加入军队就是在点分树上更新,然后记录一下第一次树的重心,然后由这个树根沿着点分树的父子关系向下搜索
所以我们还要开另外一个临界表来存储他们的儿子;
记得我们判断答案是否在他这个儿子里,要在原树上判断,一开始就在点分树上判断,WA了好几遍
然后搜索的时候,要回到点分树上搜索。。。。
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define int long long 4 #define re register int 5 const int N=100005; 6 int n,m; 7 int to[N<<1],nxt[N<<1],val[N<<1],head[N],rp; 8 void add_edg(int x,int y,int z){ 9 to[++rp]=y;val[rp]=z;nxt[rp]=head[x];head[x]=rp; 10 } 11 int siz[N],son[N],dep[N],len[N],fa[N],top[N]; 12 void dfs1(int x,int f){ 13 siz[x]=1; 14 dep[x]=dep[f]+1;fa[x]=f; 15 for(re i=head[x];i;i=nxt[i]){ 16 int y=to[i]; 17 if(y==f)continue; 18 len[y]=len[x]+val[i]; 19 dfs1(y,x); 20 siz[x]+=siz[y]; 21 if(!son[x]||siz[y]>siz[son[x]])son[x]=y; 22 } 23 } 24 void dfs2(int x,int f){ 25 top[x]=f; 26 if(son[x])dfs2(son[x],f); 27 for(re i=head[x];i;i=nxt[i]){ 28 int y=to[i]; 29 if(y==fa[x]||y==son[x])continue; 30 dfs2(y,y); 31 } 32 } 33 int get_lca(int x,int y){ 34 while(top[x]!=top[y]){ 35 if(dep[top[x]]<dep[top[y]])swap(x,y); 36 x=fa[top[x]]; 37 } 38 return dep[x]<dep[y]?x:y; 39 } 40 int get_dis(int x,int y){ 41 return len[x]+len[y]-2*len[get_lca(x,y)]; 42 } 43 int rt,alsiz,mx,ms[N]; 44 bool vis[N]; 45 void get_rt(int x,int f){ 46 siz[x]=1;ms[x]=0; 47 for(re i=head[x];i;i=nxt[i]){ 48 int y=to[i]; 49 if(y==f||vis[y])continue; 50 get_rt(y,x); 51 siz[x]+=siz[y]; 52 ms[x]=max(ms[x],siz[y]); 53 } 54 ms[x]=max(ms[x],alsiz-siz[x]); 55 if(ms[x]<mx)mx=ms[x],rt=x; 56 } 57 void get_siz(int x,int f){ 58 siz[x]=1; 59 for(re i=head[x];i;i=nxt[i]){ 60 int y=to[i]; 61 if(y==f||vis[y])continue; 62 get_siz(y,x); 63 siz[x]+=siz[y]; 64 } 65 } 66 int newfa[N]; 67 int t[N<<1],nx[N<<1],he[N<<1],va[N<<1],r; 68 void add_ed(int x,int y,int z){ 69 t[++r]=y; 70 va[r]=z; 71 nx[r]=he[x]; 72 he[x]=r; 73 } 74 void pre_dfs(int x){ 75 vis[x]=1;get_siz(x,0); 76 for(re i=head[x];i;i=nxt[i]){ 77 //cout<<"sb"<<rt<<endl; 78 int y=to[i]; 79 if(vis[y])continue; 80 alsiz=mx=siz[y];get_rt(y,0); 81 add_ed(x,y,rt); 82 newfa[rt]=x;pre_dfs(rt); 83 } 84 } 85 int sum1[N],sum2[N]; 86 int siz1[N],siz2[N]; 87 void get_up(int x,int v){ 88 for(re i=x;i;i=newfa[i])sum1[i]+=get_dis(i,x)*v,siz1[i]+=v; 89 for(re i=x;newfa[i];i=newfa[i])sum2[i]+=get_dis(newfa[i],x)*v,siz2[i]+=v; 90 } 91 int get_sum(int x){ 92 int ret=sum1[x]; 93 for(re i=x;newfa[i];i=newfa[i]){ 94 ret+=sum1[newfa[i]]; 95 ret-=sum2[i]; 96 ret+=get_dis(newfa[i],x)*(siz1[newfa[i]]-siz2[i]); 97 } 98 return ret; 99 } 100 int get_ans(int x){ 101 int ret=get_sum(x); 102 for(re i=he[x];i;i=nx[i]){ 103 if(get_sum(t[i])<ret)return get_ans(va[i]); 104 } 105 return ret; 106 } 107 signed main(){ 108 scanf("%lld%lld",&n,&m); 109 for(re i=1;i<n;i++){ 110 int x,y,z; 111 scanf("%lld%lld%lld",&x,&y,&z); 112 add_edg(x,y,z);add_edg(y,x,z); 113 } 114 dfs1(1,0); 115 dfs2(1,1); 116 alsiz=mx=n; 117 get_rt(1,0); 118 //cout<<rt<<endl; 119 pre_dfs(rt); 120 memset(vis,0,sizeof(vis)); 121 alsiz=mx=n; 122 get_rt(1,0); 123 //cout<<rt<<endl; 124 for(re i=1;i<=m;i++){ 125 int x,y; 126 scanf("%lld%lld",&x,&y); 127 get_up(x,y); 128 //cout<<rt<<" "; 129 printf("%lld\n",get_ans(rt)); 130 } 131 }View Code
所以对于点分树的题,第一找好这个题寻找的是啥
第二搞对容斥,就上面那个公式
第三打代码得细心,细节太多了
这类题多会求lca,我习惯用树链剖分,好大也好看
注意距离和深度不是一回事,找lca用深度,找dis用距离(权值);
标签:幻想,son,游戏,树状,int,top,点分,战略,树上 来源: https://www.cnblogs.com/hzoi-fengwu/p/14881811.html