BZOJ 3924: [Zjoi2015]幻想乡战略游戏 动态树分治

3924: [Zjoi2015]幻想乡战略游戏

Time Limit: 100 Sec  Memory Limit: 256 MB
Submit: 933  Solved: 430
[Submit][Status][Discuss]

Description

 傲娇少女幽香正在玩一个非常有趣的战略类游戏,本来这个游戏的地图其实还不算太大,幽香还能管得过来,但是不知道为什么现在的网游厂商把游戏的地图越做越大,以至于幽香一眼根本看不过来,更别说和别人打仗了。 在打仗之前,幽香现在面临一个非常基本的管理问题需要解决。 整个地图是一个树结构,一共有n块空地,这些空地被n-1条带权边连接起来,使得每两个点之间有一条唯一的路径将它们连接起来。在游戏中,幽香可能在空地上增加或者减少一些军队。同时,幽香可以在一个空地上放置一个补给站。 如果补给站在点u上,并且空地v上有dv个单位的军队,那么幽香每天就要花费dv×dist(u,v)的金钱来补给这些军队。由于幽香需要补给所有的军队,因此幽香总共就要花费为Sigma(Dv*dist(u,v),其中1<=V<=N)的代价。其中dist(u,v)表示u个v在树上的距离(唯一路径的权和)。 因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。

PDF版试题:JudgeOnline/upload/201708/zjoi2015d1.pdf

Input

第一行两个数n和Q分别表示树的点数和幽香操作的个数,其中点从1到n标号。 
接下来n-1行,每行三个正整数a,b,c,表示a和b之间有一条边权为c的边。 
接下来Q行,每行两个数u,e,表示幽香在点u上放了e单位个军队
(如果e<0,就相当于是幽香在u上减少了|e|单位个军队,说白了就是du←du+e)。
数据保证任何时刻每个点上的军队数量都是非负的。 
1<=c<=1000, 0<=|e|<=1000, n<=10^5, Q<=10^5
对于所有数据,这个树上所有点的度数都不超过20
N,Q>=1

Output

 对于幽香的每个操作,输出操作完成以后,每天的最小花费,也即如果幽香选择最优的补给点进行补给时的花费。 

Sample Input

10 5
1 2 1
2 3 1
2 4 1
1 5 1
2 61
2 7 1
5 8 1
7 91
1 10 1
3 1
2 1
8 1
3 1
4 1

Sample Output

0
1
4
5
6

动态树分治

最开始对于这题的信息维护极其懵逼

看来题解发现真是傻逼。。

每个分治根子树权值和,根上子树答案,上一个分治根在这可子树的答案就行了

最优点的转移竟然可以暴力出奇迹。。。


#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<complex>
#include<iostream>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<string>
#include<bitset>
#include<queue>
#include<map>
#include<set>
using namespace std;

typedef long long ll;

inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
	return x*f;
}
void print(ll x)
{if(x<0)putchar('-'),x=-x;if(x>=10)print(x/10);putchar(x%10+'0');}

const int N=100100,inf=0X3f3f3f3f;

int ecnt,last[N];
struct EDGE{int to,nt,val;}e[N<<1];
inline void add(int u,int v,int val)
{e[++ecnt]=(EDGE){v,last[u],val};last[u]=ecnt;}

int n,tot,root;

//ancestor
int anc[N][20],dep[N];

ll dis[N];

void dfs(int u)
{
	for(int i=1;(1<<i)<=dep[u];++i)anc[u][i]=anc[anc[u][i-1]][i-1];
	for(int i=last[u];i;i=e[i].nt)if(e[i].to^anc[u][0])
	{dis[e[i].to]=dis[u]+e[i].val;dep[e[i].to]=dep[u]+1;anc[e[i].to][0]=u;dfs(e[i].to);}
}

inline int getlca(int u,int v)
{
	if(dep[u]<dep[v])swap(u,v);
	int len=dep[u]-dep[v];
	for(int i=0;(1<<i)<=len;++i)if(len&(1<<i))u=anc[u][i];
	if(u==v)return u;
	for(int i=17;~i;i--)if(anc[u][i]^anc[v][i])
	u=anc[u][i],v=anc[v][i];
	return anc[u][0];
}

inline int getdis(int u,int v)
{int lca=getlca(u,v);return dis[u]+dis[v]-(dis[lca]<<1);}

bool vis[N];

int f[N],size[N];

void getroot(int u,int fa)
{
	f[u]=0;size[u]=1;
	for(int i=last[u];i;i=e[i].nt)if(e[i].to^fa&&!vis[e[i].to])
	{
		getroot(e[i].to,u);size[u]+=size[e[i].to];
		f[u]=max(f[u],size[e[i].to]);
	}
	f[u]=max(f[u],tot-size[u]);
	if(f[u]<f[root])root=u;
}

int fa[N];

void init(int u,int ffa)
{
	fa[u]=ffa;vis[u]=1;
	for(int i=last[u];i;i=e[i].nt)if(!vis[e[i].to])
	{
		root=0;tot=size[e[i].to];getroot(e[i].to,u);
		init(root,u);
	}
}

ll ans[N],ans1[N],sum[N];

void update(int u,int val)
{
	sum[u]+=val;
	for(int v=u;fa[v];v=fa[v])
	{
		ll tmp=getdis(fa[v],u);
		sum[fa[v]]+=val;
		ans1[v]+=val*tmp;
		ans[fa[v]]+=val*tmp;
	}
}

int node;

ll cal(ll u)
{
	ll res=ans[u];
	for(int v=u;fa[v];v=fa[v])
	{
		ll tmp=getdis(fa[v],u);
		res+=(ans[fa[v]]-ans1[v]);
		res+=tmp*(sum[fa[v]]-sum[v]);
	}
	return res;
}

ll query(int u)
{
	ll t=cal(u);
	for(int i=last[u];i;i=e[i].nt)
	{
		ll tmp=cal(e[i].to);
		if(tmp<t)return query(e[i].to);
	}
	node=u;
	return t;
}

int main()
{
	tot=n=read();int Q=read();
	register int i,u,v,val;
	for(i=1;i<n;++i){u=read();v=read();val=read();add(u,v,val);add(v,u,val);}
	dfs(1);f[0]=inf;getroot(1,0);node=root;init(root,0);
	while(Q--)
	{
		u=read();val=read();
		update(u,val);
		print(query(node));puts("");
	}
	return 0;
}
/*
10 5
1 2 1
2 3 1
2 4 1
1 5 1
2 6 1
2 7 1
5 8 1
7 9 1
1 10 1
3 1
2 1
8 1
3 1
4 1

0
1
4
5
6
*/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值