记录编号 159736 评测结果 AAAAAAAAAAAAAAAAAAAA
题目名称 [ZJOI 2015] 幻想乡战略游戏 最终得分 100
用户昵称 Gravatarcstdio 是否通过 通过
代码语言 C++ 运行时间 25.349 s
提交时间 2015-04-22 16:11:36 内存使用 26.07 MiB
显示代码纯文本
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
typedef long long LL;
const int SIZEN=100010;
//重要全局变量:
int N,Q;
vector<pair<int,int> > c[SIZEN];
int DC_root;
vector<pair<int,int> > DC_son[SIZEN];//first是重心,second是连向边
int DC_father[SIZEN];


//读入组件:
void add_edge(int a,int b,int w){
	c[a].push_back(make_pair(b,w));
}
void read(void){
	scanf("%d%d",&N,&Q);
	int a,b,c;
	for(int i=1;i<N;i++){
		scanf("%d%d%d",&a,&b,&c);
		add_edge(a,b,c);
		add_edge(b,a,c);
	}
}


//距离运算组件:
int depth[SIZEN]={0};
int DFS_lis[SIZEN*2],DFS_len=0;
int dfn[SIZEN]={0};
bool vis[SIZEN]={0};
void DFS(int x){
	DFS_len++;
	dfn[x]=DFS_len;
	DFS_lis[DFS_len]=x;
	vis[x]=true;
	for(int i=0;i<c[x].size();i++){
		int u=c[x][i].first;
		if(vis[u]) continue;
		depth[u]=depth[x]+c[x][i].second;
		DFS(u);
		DFS_lis[++DFS_len]=x;
	}
}
int ST[SIZEN*2][20]={0};
int log_2[SIZEN*2]={0};
int get_log(int n){
	int ans=-1;
	while(n) ans++,n>>=1;
	return ans;
}
int get_shallower(int x,int y){
	return depth[x]<depth[y]?x:y;
}
void ST_prepare(void){
	for(int i=1;i<=DFS_len;i++) log_2[i]=get_log(i);
	for(int i=1;i<=DFS_len;i++) ST[i][0]=DFS_lis[i];
	for(int j=1;j<=log_2[DFS_len];j++){
		for(int i=1;i<=DFS_len;i++){
			if(i+(1<<j)-1<=DFS_len){
				ST[i][j]=get_shallower(ST[i][j-1],ST[i+(1<<(j-1))][j-1]);
			}
		}
	}
}
void prepare_dist(void){
	DFS(1);
	ST_prepare();
}
int get_dist(int a,int b){
	int l=dfn[a],r=dfn[b];
	if(l>r) swap(l,r);
	int m=log_2[r-l+1];
	int g=get_shallower(ST[l][m],ST[r-(1<<m)+1][m]);
	return depth[a]+depth[b]-2*depth[g];
}


//分治结构组件:
bool DC_decided[SIZEN]={0};
int sub_size[SIZEN]={0};//子树大小
int mx_sub[SIZEN]={0};//分裂以后的最大块
int now_root;
void DC_calc_size(int x,int fa){
	sub_size[x]=1;
	for(int i=0;i<c[x].size();i++){
		int u=c[x][i].first;
		if(DC_decided[u]||u==fa) continue;
		DC_calc_size(u,x);
		sub_size[x]+=sub_size[u];
	}
}
void DC_select_root(int x,int upcnt,int fa){
	mx_sub[x]=upcnt;
	for(int i=0;i<c[x].size();i++){
		int u=c[x][i].first;
		if(DC_decided[u]||u==fa) continue;
		mx_sub[x]=max(mx_sub[x],sub_size[u]);
		DC_select_root(u,upcnt+sub_size[x]-sub_size[u],x);
	}
	if(now_root==-1||mx_sub[x]<mx_sub[now_root]) now_root=x;
}
int DC_DFS(int x){//搞出x所在连通块内的根
	DC_calc_size(x,0);
	now_root=-1;
	DC_select_root(x,0,0);
	int rt=now_root;
	DC_decided[rt]=true;
	for(int i=0;i<c[rt].size();i++){
		int u=c[rt][i].first;
		if(DC_decided[u]) continue;
		int v=DC_DFS(u);
		DC_son[rt].push_back(make_pair(v,u));
		DC_father[v]=rt;
	}
	return rt;
}


//修改和查询组件:
LL sub_cost[SIZEN];//给本层连通块补给的代价
LL sub_sum[SIZEN];//本层连通块的大小
LL up_ctb[SIZEN];//这一连通块对上一层重心补给代价的贡献
void modify(int x,LL w){//x节点的军队数+=w
	int rt=x;
	while(rt){
		sub_cost[rt]+=get_dist(rt,x)*w;//对rt的贡献
		sub_sum[rt]+=w;
		if(rt!=DC_root) up_ctb[rt]+=get_dist(DC_father[rt],x)*w;//对rt的上级重心的贡献
		rt=DC_father[rt];
	}
}
LL all_cost[SIZEN];//辅助数组,意为给全部节点补给的代价
LL unsel_cost[SIZEN],unsel_sum[SIZEN];
void walk_down(int x,int u){//从重心x走向次级重心u
	unsel_cost[x]=sub_cost[x]-up_ctb[u];
	unsel_sum[x]=sub_sum[x]-sub_sum[u];
	all_cost[u]=sub_cost[u];
	int rt=x;
	while(rt){
		all_cost[u]+=unsel_cost[rt]+unsel_sum[rt]*get_dist(rt,u);
		rt=DC_father[rt];
	}
}
LL calc_real_son(int x,int v,int u){//计算v的代价,其中v是x的树儿子,所在重心是u
	unsel_cost[x]=sub_cost[x]-up_ctb[u];
	unsel_sum[x]=sub_sum[x]-sub_sum[u];
	LL ans=0;
	int rt=x;
	while(rt){
		ans+=unsel_cost[rt]+unsel_sum[rt]*get_dist(rt,v);
		rt=DC_father[rt];
	}
	ans+=up_ctb[u]-sub_sum[u]*get_dist(x,v);
	return ans;
}
LL query(int x){//在x子树中寻找答案,此时all_cost[x]已经求出
	LL ans=all_cost[x];
	int id=-1;
	for(int i=0;i<DC_son[x].size();i++){
		int rt=DC_son[x][i].first,u=DC_son[x][i].second;
		all_cost[u]=calc_real_son(x,u,rt);
		if(all_cost[u]<ans){
			ans=all_cost[u];
			id=rt;
		}
	}
	if(id!=-1){
		walk_down(x,id);
		ans=query(id);
	}
	return ans;
}
LL query(void){
	all_cost[DC_root]=sub_cost[DC_root];
	return query(DC_root);
}


int main(){
	freopen("zjoi15_tree.in","r",stdin);
	freopen("zjoi15_tree.out","w",stdout);
	read();//读入
	prepare_dist();//加载距离运算套装
	DC_root=DC_DFS(1);//建立分治结构
	int a,w;
	for(int i=1;i<=Q;i++){
		scanf("%d%d",&a,&w);
		modify(a,w);
		printf("%lld\n",query());
	}
	return 0;
}