P3345 [ZJOI2015]幻想乡战略游戏
Posted andyc_03
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P3345 [ZJOI2015]幻想乡战略游戏相关的知识,希望对你有一定的参考价值。
【题意】
给定一个树,要求如下操作
每次修改一个点权之后,询问选择树上哪一个点作为补给站u可以使dis(u,i)*vi的和最小
【分析】
直接考虑修改点权会带来什么影响,建好点分树以后
我们对于每个点记录dis1表示子树内对自己的贡献,dis2表示子树内到fa的贡献,sumv表示子树的点权和,每次跳fa来修改sumv、dis1、dis2
在询问的时候,考虑从点分树的根节点开始,先计算自己节点的答案,然后计算各个儿子的答案,如果有更优的便往下走,直到不存在更优的返回答案即可
这样做是基于一种贪心的思想,考虑如果v是u的儿子,v比u更优,这样的儿子最多有一个
也就是说如果现在节点的子树dv和*2大于总节点,那么向那个方向过去一定比原方案好
【代码】
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=1e5+5; int n,m,head[maxn],tot; struct edge { int to,nxt,v; }e[maxn<<1],dfse[maxn<<1]; void add(int x,int y,int z) { e[++tot].to=y; e[tot].nxt=head[x]; e[tot].v=z; head[x]=tot; } int h[maxn],cnt; void dfsadd(int x,int y,int z) { dfse[++cnt].to=y; dfse[cnt].nxt=h[x];dfse[cnt].v=z; h[x]=cnt; } int st[maxn<<1][20],dis[maxn],dfn[maxn],dfstimes; void dfs(int u,int fa) { dfn[u]=++dfstimes; st[dfn[u]][0]=dis[u]; for(int i=head[u];i;i=e[i].nxt) { int to=e[i].to; if(to==fa) continue; dis[to]=dis[u]+e[i].v; dfs(to,u); st[++dfstimes][0]=dis[u]; } } int lg[maxn<<1],cs; void lca_init() { lg[0]=-1; for(int i=1;i<=n*2;i++) lg[i]=lg[i>>1]+1; while((1<<(cs+1))<=n*2) cs++; for(int j=1;j<=cs;++j) for(int i=1;i+(1<<j)-1<=(n<<1);++i) st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]); } int root,size,gsiz,siz[maxn],vis[maxn]; void findrt(int u,int fa) { siz[u]=1; int maxsiz=0; for(int i=head[u];i;i=e[i].nxt) { int to=e[i].to; if(to==fa || vis[to]) continue; findrt(to,u); maxsiz=max(maxsiz,siz[to]); siz[u]+=siz[to]; } maxsiz=max(maxsiz,size-siz[u]); if(maxsiz<gsiz) { gsiz=maxsiz; root=u; } } int fa[maxn]; void solve(int u) { vis[u]=1; for(int i=head[u];i;i=e[i].nxt) { int to=e[i].to; if(vis[to]) continue; root=0; size=gsiz=siz[to]; findrt(to,0); dfsadd(u,root,to); fa[root]=u; solve(root); } } ll sumv[maxn],dis1[maxn],dis2[maxn]; ll lcadis(int x,int y) { if(dfn[x]>dfn[y]) swap(x,y); int i=lg[dfn[y]-dfn[x]+1]; return min(st[dfn[x]][i],st[dfn[y]-(1<<i)+1][i]); } ll calcdis(int x,int y) { return dis[x]+dis[y]-2*lcadis(x,y); } void update(int x,int y) { sumv[x]+=y; for(int i=x;fa[i];i=fa[i]) { ll dist=calcdis(x,fa[i])*y; dis1[fa[i]]+=dist; dis2[i]+=dist; sumv[fa[i]]+=y; } } ll calc(int u) { ll ans=dis1[u]; for(int p=u;fa[p];p=fa[p]) { ll dist=calcdis(fa[p],u); ans+=dis1[fa[p]]-dis2[p]; ans+=dist*(sumv[fa[p]]-sumv[p]); } return ans; } ll query(int u) { ll ans=calc(u); for(int i=h[u];i;i=dfse[i].nxt) { ll tmp=calc(dfse[i].v); if(tmp<ans) return query(dfse[i].to); } return ans; } int main() { scanf("%d%d",&n,&m); int x,y,z; for(int i=1;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } dfs(1,0); lca_init(); size=gsiz=n; findrt(1,0); int lsrt=root; solve(root); for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); update(x,y); printf("%lld\\n",query(lsrt)); } return 0; }
以上是关于P3345 [ZJOI2015]幻想乡战略游戏的主要内容,如果未能解决你的问题,请参考以下文章
luogu P3345 [ZJOI2015]幻想乡战略游戏 |动态点分治