luogu_P3345[zjoi2015]幻想乡战略游戏

传送门

Description

傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了。

在打仗之前,幽香现在面临一个非常基本的管理问题需要解决。 整个地图是一个树结构,一共有(n)块空地,这些空地被(n-1)条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来。

在游戏中,幽香可能在空地上增加或者减少一些军队。同时,幽香可以在一个空地上放置一个补给站。 如果补给站在点(u)上,并且空地v上有(dv)个单位的军队,那么幽香每天就要花费(dv*dist(u,v))的金钱来补给这些军队。

由于幽香需要补给所有的军队,因此幽香总共就要花费为(sum Dv*dist(u,v)) ((1leq V leq N))的代价。其中(dist(u,v))表示u个v在树上的距离(唯一路径的权和)。

因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。

但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。

Solution

题意:树上点权修改,询问带权重心

大大加深了对点分树的理解?

首先,点分树有个性质:点分树上的两点的lca在原树的两点路径上,这样,我们通过记录点分树上一点到它的祖先的在原树上的距离,就可以得到任意两点在原树上的距离啦

带权重心是什么咧?

可以当做它是这样一个点,已这个点为根的每个子树的权重都不超过原树的总size的一半。

为什么?假如找到了这个点,那么无论把它移动进入哪个子树,代价都会变得更大,读者可以自行证明。

而我们用上面的做法,就可以得到树的带权重心。

对于这道题:

  • 先预处理出点分树,记录下每个点在点分树上的孩子和祖先以及和每个祖先的实际距离(便于以后查找)

  • 记录下点分树上每个节点,在原树上所代表的联通块的根节点(HR)(这里指的是深度最小的那个点)

    为什么要记录这个东西咧?

    因为我们在寻找带权重心的时候,每进入一个点,就要更改某些点的权重,因为要算上除了这个联通块外的权重,而需要更改的点就是这个(HR),可以理解为,它是这个联通块的边界

    当然找到重心后,你得把你做的更改全都改回来

  • 我们考虑每次更改会有什么影响,首先,总权重增加(e),点分树上的相关节点的总权重也会发生变化,还有就是要即使更新相关节点的代价和,具体的,加上(e*dis),这里的相关节点指的是修改节点的祖先节点(我们显然只需要记录它对祖先的贡献,根据点分树的性质)

  • 查询就是枚举这个点与重心的在点分树上的(lca),统计代价即可

因为点分树的深度不超过(log n),所以总复杂度是(O(n log^2 n))


Code 

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
#define MN 100005
#define reg register
int n,m,en,hr[MN];
struct edge{int to,w,nex;}e[MN<<1];
inline void ins(int f,int t,int w)
{
	e[++en]=(edge){t,w,hr[f]};hr[f]=en;
	e[++en]=(edge){f,w,hr[t]};hr[t]=en;
}
bool vis[MN];
int rt,sm,siz[MN],dep[MN];
ll vsum,w[MN],val[MN];
struct Node{int id;ll dis;};
std::vector<Node> fa[MN],s[MN];
int HR[MN];
void pre_dfs(int x,int f)
{
	reg int i;siz[x]=1;
	for(i=hr[x];i;i=e[i].nex)if(!vis[e[i].to]&&(f^e[i].to))
		pre_dfs(e[i].to,x),siz[x]+=siz[e[i].to];
}
void getroot(int x,int f)
{
	reg int i;rt=x;
	for(i=hr[x];i;i=e[i].nex)
	if(!vis[e[i].to]&&(f^e[i].to)&&(siz[e[i].to]<<1)>=sm){getroot(e[i].to,x);break;}
}
void dfs(int x,int f,int ro)
{
	fa[x].push_back((Node){ro,dep[x]});
	reg int i;
	for(i=hr[x];i;i=e[i].nex)if(!vis[e[i].to]&&(f^e[i].to))
		dep[e[i].to]=dep[x]+e[i].w,dfs(e[i].to,x,ro);
}
inline void pre_work(int x=1,int f=0)
{
	pre_dfs(x,f);
	sm=siz[x];getroot(x,f);vis[rt]=true;
	reg int i,j=rt;
	s[f].push_back((Node){j,0LL});fa[j].push_back((Node){j,0LL});
	HR[j]=x;
	for(i=hr[rt];i;i=e[i].nex)
		if(!vis[e[i].to]){dep[e[i].to]=e[i].w;dfs(e[i].to,j,j);pre_work(e[i].to,j);}
	rt=j;
}
inline void Modify(int x,int y)
{
	vsum+=y;val[x]+=y;reg int i,j=x,f,k;
	for(i=fa[x].size()-1;~i;--i) w[fa[x][i].id]+=y;
	for(i=fa[x].size()-2;~i;--i)
		for(j=s[fa[x][i].id].size()-1;~j;--j)
		if(s[fa[x][i].id][j].id==fa[x][i+1].id){s[fa[x][i].id][j].dis+=y*fa[x][i].dis;break;}
}
inline void upd(int x,int y){for(reg int i=fa[x].size()-1;~i;--i)w[fa[x][i].id]+=y;}
inline int Find(int x)
{
    reg int ret=x,i,tmp;
    for(i=s[x].size()-1;~i;--i)
        if(w[s[x][i].id]*2>=vsum)
        {
            tmp=w[x]-w[s[x][i].id];
            upd(HR[s[x][i].id],tmp);ret=Find(s[x][i].id);upd(HR[s[x][i].id],-tmp);
			break;
        }
	return ret;
}
inline ll Query(int x)
{
    reg ll ret=0;
	reg int i,j,las=0;
    for(i=fa[x].size()-1;~i;las=fa[x][i].id,--i)
    {
        ret+=fa[x][i].dis*val[fa[x][i].id];
        for(j=s[fa[x][i].id].size()-1;~j;--j)
            if(s[fa[x][i].id][j].id^las)
				ret+=fa[x][i].dis*w[s[fa[x][i].id][j].id]+s[fa[x][i].id][j].dis;
    }
	return ret;
}
int main()
{
	n=read();m=read();
	register int i,x,y;
    for(i=1;i<n;++i) x=read(),y=read(),ins(x,y,read());
    pre_work();
    while(m--)
    {
    	x=read();y=read();Modify(x,y);
    	printf("%lld
",Query(Find(rt)));
	}
	return 0;
}


Blog来自PaperCloud,未经允许,请勿转载,TKS!

原文地址:https://www.cnblogs.com/PaperCloud/p/10213065.html