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

9 篇文章 0 订阅
8 篇文章 0 订阅

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

Time Limit: 100 Sec   Memory Limit: 256 MB
Submit: 727   Solved: 331
[ 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在树上的距离(唯一路径的权和)。 因为游戏的规定,幽香只能选择一个空地作为补给站。在游戏的过程中,幽香可能会在某些空地上制造一些军队,也可能会减少某些空地上的军队,进行了这样的操作以后,出于经济上的考虑,幽香往往可以移动他的补给站从而省一些钱。但是由于这个游戏的地图是在太大了,幽香无法轻易的进行最优的安排,你能帮帮她吗? 你可以假定一开始所有空地上都没有军队。 

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)。数据保证任何时刻每个点上的军队数量都是非负的。 

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

HINT

对于所有数据,1<=c<=1000, 0<=|e|<=1000, n<=105, Q<=105

Source

[ Submit][ Status][ Discuss]

写了很久又调了很久的一道题。。。
每次操作完之后,补给站的建设位置显然是这棵树的点加权重心
不妨采用反证法,假设一个非加权重心x建立补给站最优
那么以x为根,它的儿子中必有一个节点,子树点权和超过总权值一半
如果把x往这个方向移动,经过这条边的军队肯定边少了,所以答案可以更优
因此,只要写某种数据结构,支持在快速修改点权的前提下快速求出重心以及答案,就行了

大的方向是点分树,首先将它构造出来

从根开始查询,点分树中每个点控制的是原树上的一个树连通块
假设在点分树上分治时,能够维护好每个儿子控制的子树点权,
这样,每次寻找子树点权最大的那个儿子移动过去,直到出现子树点权最大也不超过总权和一半的情况
这时,我们就找到了当前询问下的加权中心
不过每个儿子控制的子树点权并不好维护,因为除了当前连通块,还有与外界连通的部分
在经历了无数次失败。。(一下午 + 半个晚上)终于试出一个不错的模型

假设三角形内部是重心a所维护的连通块,在点分树中,a的父亲是c,c与a连通块中的b右边相连
这样,转移的时候,就将整个与c连通且不与a连通块有任何接触的点的点权缩成一个大的点C
把这个新点挂在b下面,在查找a的儿子中点权最大的子树时,先将C的影响添加给b所在的子树就行了
这种方法转移写起来难度不大,并且可以保证正确性
以及,,,苟蒻目前为止只构造出了额外点最多为3个的情形。。。
所以直观感觉额外点不会很多吧??(撑死logn个行不行????)
查询儿子中点权最大的点的话,我是点分树中每个点额外开个可修改堆做的
查询的时候先把额外点的点权加进去并修改,查询完再弄回来

既然找到了重心,统计答案就不是什么难事了,逐层往上就行
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;

const int maxn = 1E5 + 10;
const int T = 17;
const int INF = ~0U>>1;
typedef long long LL;

struct E{
	int to,w; E(){}
	E(int to,int w): to(to),w(w){}
};

struct Node{
	int Num,tot; Node(){}
	Node(int Num,int tot): Num(Num),tot(tot){}
};

struct data{
	int Num,sum; data(){}
	data(int Num,int sum): Num(Num),sum(sum){}
	bool operator < (const data &B) const {return sum < B.sum;}
};
typedef __gnu_pbds::priority_queue<data,less<data>,__gnu_pbds::pairing_heap_tag> Heap;

int n,m,rt,Max,O,TOT,siz[maxn],Fa[maxn];
bool Huge[maxn]; LL SUM[maxn],Sizsum[maxn];

Heap Q[maxn];
vector <Node> h;
vector <E> v[maxn];
vector <LL> sum[maxn];
vector <int> Dis[maxn],Num[maxn],g[maxn],Siz[maxn],G[maxn];
vector <Heap::point_iterator> id[maxn];

int getint()
{
	char ch = getchar(); int ret = 0,a = 1;
	while (ch < '0' || '9' < ch)
	{
		if (ch == '-') a = -1;
		ch = getchar();
	}
	while ('0' <= ch && ch <= '9')
		ret = ret * 10 + ch - '0',ch = getchar();
	return ret * a;
}

void Dfs1(int x,int from,int tot)
{
	int ma = 0; siz[x] = 1;
	for (int i = 0; i < v[x].size(); i++)
	{
		E e = v[x][i];
		if (e.to == from || Huge[e.to]) continue;
		Dfs1(e.to,x,tot);
		siz[x] += siz[e.to];
		ma = max(ma,siz[e.to]);
	}
	ma = max(ma,tot - siz[x]);
	if (ma < Max) Max = ma,O = x;
}

void Dfs2(int x,int o,int cnt,int tot,int from)
{
	Dis[x].push_back(tot); Num[x].push_back(cnt);
	for (int i = 0; i < v[x].size(); i++)
	{
		E e = v[x][i];
		if (Huge[e.to] || e.to == from) continue;
		Dfs2(e.to,o,cnt,tot + e.w,x);
	}
}

int Work(int x,int tot)
{
	Max = INF; Dfs1(x,0,tot);
	int o = O,cnt = 0,dismin = INF; Huge[o] = 1;
	for (int i = 0; i < v[o].size(); i++)
	{
		E e = v[o][i]; if (Huge[e.to]) continue;
		Dfs2(e.to,o,cnt,e.w,0);
		sum[o].push_back(0); Siz[o].push_back(0);
		int ret = Work(e.to,siz[e.to]); Fa[ret] = o;
		g[o].push_back(ret); G[o].push_back(e.to);
		id[o].push_back(Q[o].push(data(cnt++,0)));
	}
	Num[o].push_back(-1); return o;
}

void Modify(int o,int x,int now,int t)
{
	if (o == x) {Sizsum[o] += t; return;}
	int num = Num[x][now];
	Siz[o][num] += t; Sizsum[o] += t;
	sum[o][num] += 1LL * Dis[x][now] * t;
	SUM[o] += 1LL * Dis[x][now] * t;
	Q[o].modify(id[o][num],data(num,Siz[o][num]));
	Modify(g[o][num],x,now + 1,t);
}

void Search(int o,vector <Node> &h,int now)
{
	if (Q[o].empty()) {O = o; return;}
	for (int i = 0; i < h.size(); i++)
	{
		int num = Num[h[i].Num][now];
		if (num == -1) continue; Siz[o][num] += h[i].tot;
		Q[o].modify(id[o][num],data(num,Siz[o][num]));
	}
	data k = Q[o].top();
	if (k.sum <= TOT / 2)
	{
		for (int i = 0; i < h.size(); i++)
		{
			int num = Num[h[i].Num][now];
			if (num == -1) continue; Siz[o][num] -= h[i].tot;
			Q[o].modify(id[o][num],data(num,Siz[o][num]));
		}
		O = o; return;
	}
	int Nex = k.Num,ss = 0;
	vector <Node> h0; h0.clear();
	for (int i = 0; i < h.size(); i++)
	{
		int num = Num[h[i].Num][now];
		if (num != -1)
		{
			Siz[o][num] -= h[i].tot;
			Q[o].modify(id[o][num],data(num,Siz[o][num]));
		}
		if (num == Nex) h0.push_back(h[i]);
		else ss += h[i].tot;
	}
	ss += Sizsum[o] - Siz[o][Nex];
	if (ss) h0.push_back(Node(G[o][Nex],ss));
	Search(g[o][Nex],h0,now + 1);
}

LL Calc(int o,int x,int now)
{
	if (o == x) return o == rt ? SUM[o] : SUM[o] + Calc(Fa[o],x,now - 1);
	else
	{
		int num = Num[x][now];
		LL Now = 1LL * Dis[x][now] * (Sizsum[o] - Siz[o][num]);
		Now += (SUM[o] - sum[o][num]);
		return Fa[o] ? Now + Calc(Fa[o],x,now - 1) : Now;
	}
}

char ch[20];
void Print(LL x)
{
	if (!x) {puts("0"); return;} int len = 0;
	while (x) ch[++len] = x % 10,x /= 10;
	for (int i = len; i; i--) putchar(ch[i] + '0'); puts("");
}

int main()
{
	//freopen("DMC.txt","r",stdin);
	//freopen("1.out","w",stdout);
	
	n = getint(); m = getint();
	for (int i = 1; i < n; i++)
	{
		int x = getint(),y,w;
		y = getint(); w = getint();
		v[x].push_back(E(y,w));
		v[y].push_back(E(x,w));
	}
	rt = Work(1,n);
	for (int I = 1; I <= m; I++)
	{
		int x = getint(),t = getint();
		TOT += t; Modify(rt,x,0,t);
		h.clear(); Search(rt,h,0);
		Print(Calc(O,O,Dis[O].size()));
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值