洛谷3345:幻想乡战略游戏(动态树分治)
2017-10-18 16:49
381 查看
现在终于下午停课了,就是说我只要熬过早上就可以尽情怠惰专心学习了。
题面
由于我还没入东方,所以就由我来粗鄙地口胡题意吧:
一棵n个点的树,边有长度,点有点权d。每次更改一个点的点权,然后让你找一个点x,最小化∑i=1ndis(i,x)∗d[i]输出这个最小值。
经过一轮膜大佬的题解,我发现了原来有一个叫带权重心的东西。本题的点x就是带权重心。
定义为删掉该点后,得到的连通块权值和最大的最小的点。
性质为所有点到它的带权距离和最小。
设siz[x]为子树x的权值和。
对于原树的点x和x的儿子son。若siz[son]*2>total。则带权重心在子树son中。若没有这样的儿子,则x即为带权重心。由此思想,我们可以由根开始一路往下找,直到没有这样儿子的点为止。
这样貌似就可以ac。这个算法随着树的深度增大就变得灰常慢。所以我们需要减小树的深度,据此就想到点分树。
我们先构出原树的不带权点分树,siz[x]以为x为重心的连通块的权值和。对于点x和x在点分树上的儿子son,若siz[son]*2>total。则带权重心在连通块son中。
x指向连通块son的边指向u,把点分树上u到son路径上的点的siz全部加上(siz[x]-siz[son])。再去连通块son中找,找到后再把加上的减回去。
考虑如何统计答案。我们还要再维护几个信息,设p[x]为x在点分树上的父亲,记h[x]为仅考虑x的连通块,选p[x]时的答案。g[x]为x在点分树上所有儿子的h之和。
设带权重心为x,由x一直往点分树的父亲上走,设当前走到y
g[p[y]]-h[y]+dis(p[y],x)*(siz[p[y]]-siz[x]) 可以贡献答案。
据说要用一种O(1)求lca的才不超时,具体是这样的:
在树的欧拉序中,x和y第一次出现的位置之间的序列中深度最小的点就是x和y的lca。可用RMQ维护。
复杂度Nlog2N。
如果你们看新番《如果有…》,看到有弹幕说“嫉妒使我旋转卡壳,嫉妒使我状态压缩…”。那也许就是本蒟蒻发的。
<
a5db
img src="https://img-blog.csdn.net/20171018185742211?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcTU4MjExNjg1OQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描述" title="">
题面
由于我还没入东方,所以就由我来粗鄙地口胡题意吧:
一棵n个点的树,边有长度,点有点权d。每次更改一个点的点权,然后让你找一个点x,最小化∑i=1ndis(i,x)∗d[i]输出这个最小值。
经过一轮膜大佬的题解,我发现了原来有一个叫带权重心的东西。本题的点x就是带权重心。
定义为删掉该点后,得到的连通块权值和最大的最小的点。
性质为所有点到它的带权距离和最小。
设siz[x]为子树x的权值和。
对于原树的点x和x的儿子son。若siz[son]*2>total。则带权重心在子树son中。若没有这样的儿子,则x即为带权重心。由此思想,我们可以由根开始一路往下找,直到没有这样儿子的点为止。
这样貌似就可以ac。这个算法随着树的深度增大就变得灰常慢。所以我们需要减小树的深度,据此就想到点分树。
我们先构出原树的不带权点分树,siz[x]以为x为重心的连通块的权值和。对于点x和x在点分树上的儿子son,若siz[son]*2>total。则带权重心在连通块son中。
x指向连通块son的边指向u,把点分树上u到son路径上的点的siz全部加上(siz[x]-siz[son])。再去连通块son中找,找到后再把加上的减回去。
考虑如何统计答案。我们还要再维护几个信息,设p[x]为x在点分树上的父亲,记h[x]为仅考虑x的连通块,选p[x]时的答案。g[x]为x在点分树上所有儿子的h之和。
设带权重心为x,由x一直往点分树的父亲上走,设当前走到y
g[p[y]]-h[y]+dis(p[y],x)*(siz[p[y]]-siz[x]) 可以贡献答案。
据说要用一种O(1)求lca的才不超时,具体是这样的:
在树的欧拉序中,x和y第一次出现的位置之间的序列中深度最小的点就是x和y的lca。可用RMQ维护。
复杂度Nlog2N。
#include <iostream> #include <fstream> #include <algorithm> #include <cmath> #include <ctime> #include <cstdio> #include <cstdlib> #include <cstring> #include <queue> using namespace std; #define mmst(a, b) memset(a, b, sizeof(a)) #define mmcp(a, b) memcpy(a, b, sizeof(b)) typedef long long LL; const int N=200200,oo=1e9; void read(int &hy) { hy=0; char cc=getchar(); while(cc<'0'||cc>'9') cc=getchar(); while(cc>='0'&&cc<='9') { hy=(hy<<3)+(hy<<1)+cc-48; cc=getchar(); } } int to ,nex ,head ,son ,cnt; int n,m,bigroot; bool vis ; int w ,tim ,times; int ff ,p ,siz ,gg ,sum,root; int b[2*N],er[20],rmq[2*N][19]; LL val ,dep ,f ,g ,h ,sumd; LL ans; void add(int u,int v,LL va) { to[++cnt]=v; val[cnt]=va; nex[cnt]=head[u]; head[u]=cnt; } void ljdfs(int x,int fa) { w[++times]=x; tim[x]=times; for(int h=head[x];h;h=nex[h]) if(to[h]!=fa) { dep[to[h]]=dep[x]+val[h]; ljdfs(to[h],x); w[++times]=x; } } void pre() { ljdfs(1,1); er[0]=1; for(int i=1;i<=20;i++) er[i]=er[i-1]*2; b[1]=0; for(int i=2;i<=times;i++) if(er[b[i-1]]*2<=i) b[i]=b[i-1]+1; else b[i]=b[i-1]; dep[0]=oo; for(int i=1;i<=times;i++) rmq[i][0]=w[i]; for(int j=1;j<=18;j++) for(int i=1;i<=times;i++) if(dep[rmq[i][j-1]]<dep[rmq[i+er[j-1]][j-1]]) rmq[i][j]=rmq[i][j-1]; else rmq[i][j]=rmq[i+er[j-1]][j-1]; } LL dis(int x,int y) { if(x==y) return 0; if(tim[x]>tim[y]) swap(x,y); int hy=b[tim[y]-tim[x]]; int lca; if(dep[rmq[tim[x]][hy]]<dep[rmq[tim[y]-er[hy]+1][hy]]) lca=rmq[tim[x]][hy]; else lca=rmq[tim[y]-er[hy]+1][hy]; return dep[x]+dep[y]-2*dep[lca]; } void dfs(int x,int fa) { ff[x]=fa; gg[x]=0; siz[x]=1; for(int h=head[x];h;h=nex[h]) if(to[h]!=fa&&!vis[to[h]]) { dfs(to[h],x); siz[x]+=siz[to[h]]; gg[x]=max(gg[x],siz[to[h]]); } gg[x]=max(gg[x],sum-siz[x]); if(gg[x]<gg[root]) root=x; } void dfs2(int x) { siz[ff[x]]=sum-siz[x]; vis[x]=1; for(int h=head[x];h;h=nex[h]) if(!vis[to[h]]) { sum=siz[to[h]]; root=0; dfs(to[h],to[h]); p[root]=x; son[h]=root; dfs2(root); } } void update(int x,LL ad) { sumd+=ad; for(int now=x;now;now=p[now]) { f[now]+=ad; if(p[now]) { LL len=dis(p[now],x); h[now]+=len*ad; g[p[now]]+=len*ad; } } } void find(int x) { for(int h=head[x];h;h=nex[h]) if(son[h]&&f[son[h]]*2>sumd) { int hy=f[x]-f[son[h]]; for(int now=to[h];now!=son[h];now=p[now]) f[now]+=hy; f[son[h]]+=hy; find(son[h]); for(int now=to[h];now!=son[h];now=p[now]) f[now]-=hy; f[son[h]]-=hy; return; } root=x; } int main() { cin>>n>>m; for(int i=1;i<n;i++) { int u,v,va; read(u); read(v); read(va); add(u,v,va); add(v,u,va); } pre(); gg[0]=oo; sum=n; dfs(1,1); bigroot=root; dfs2(root); while(m--) { int u; LL v; read(u); scanf("%lld",&v); update(u,v); find(bigroot); ans=g[root]; for(int now=root;p[now];now=p[now]) ans+=g[p[now]]-h[now]+dis(p[now],root)*(f[p[now]]-f[now]); printf("%lld\n",ans); } return 0; }
如果你们看新番《如果有…》,看到有弹幕说“嫉妒使我旋转卡壳,嫉妒使我状态压缩…”。那也许就是本蒟蒻发的。
<
a5db
img src="https://img-blog.csdn.net/20171018185742211?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcTU4MjExNjg1OQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="这里写图片描述" title="">
相关文章推荐
- 洛谷P3345 [ZJOI2015]幻想乡战略游戏(动态点分治,树的重心,二分查找,Tarjan-LCA,树上差分)
- 洛谷P3345:[ZJOI2015]幻想乡战略游戏 (动态点分治)
- BZOJ.3924.[ZJOI2015]幻想乡战略游戏(动态点分治)
- BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)
- bzoj3924 [Zjoi2015]幻想乡战略游戏(动态点分治)
- bzoj3924: [Zjoi2015]幻想乡战略游戏 //动态点分治
- BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)
- [BZOJ3924][Zjoi2015]幻想乡战略游戏(动态点分治)
- 【ZJOI 2015 幻想乡战略游戏】【动态点分治】
- 【bzoj3924】[Zjoi2015]幻想乡战略游戏 动态点分治
- [BZOJ3924][ZJOI2015]幻想乡战略游戏(动态点分治)
- 【BZOJ3924】幻想乡战略游戏(动态点分治)
- [ZJOI2015][bzoj3924] 幻想乡战略游戏 [动态点分治]
- 【BZOJ3924】幻想乡战略游戏(动态点分治)
- [Luogu3345][ZJOI2015]幻想乡战略游戏
- 【bzoj3924&&luogu3345】幻想乡战略游戏
- 【BZOJ3924】【Zjoi2015】幻想乡战略游戏(树链剖分+点分治)
- bzoj3924 幻想乡战略游戏 树链剖分&分治树
- AC日记——[ZJOI2015]幻想乡战略游戏 洛谷 P3345
- NKOJ 3254 (ZJOI 2015)幻想乡战略游戏(点分治)