【NOIP2018模拟赛2018.10.18】开荒

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41709770/article/details/83153584

题目

Description

题目背景: 尊者神高达作为一个萌新,在升级路上死亡无数次后被一只大黄叽带回了师门。他加入师门后发现有无穷无尽的师兄弟姐妹,这几天新副本开了,尊者神高达的师门作为一个 pve师门,于是他们决定组织一起去开荒。

题目描述: 师门可以看做以 1 为根的一棵树,师门中的每一个人都有一定的装备分数。一共会有 q 个事件。每个事件可能是一次开荒,也可能是因为开荒出了好装备而导致一个人的装分出现了变化。对于一次开荒,会有 k 个人组织,由于师门的号召力很强,所以所有在组织者中任意两个人简单路径上的人都会参加。

Input

第一行 n ,q; 接下来 1 行 n 个数,代表每个人的分值; 接下来 n-1 行 u,v 代表一条边 接下来 q 行 Q 代表询问,接下来 k 个数代表组织的人数,读入为 0时停止读入。 C 代表修改,输入 x,w 代表将 x 的分值变为 w

Output

共 Q 的数量行,为开荒的人的总分值

Sample Input

4 4

10 5 2 2

1 2

2 3

2 4

Q 3 4 0

C 3 200

Q 3 4 0

Q 1 4 0

Sample Output

9

207

17

样例解释:

第一次询问,参加的人有 2,3,4 5+2+2=9

第一次修改,权值为 10 5 200 2

第二次询问,参加的人有 2,3,4 5+200+2=207

第三次询问,参加的人有 1,2,4 10+5+2=17

Data Constraint

数据范围:

20%的数据 n<=10000,q<=500;

另外 20%的数据 k=2

另外 20%的数据 没有修改操作

所有数据 n,q<=100000,所有询问 k 的和<=1000000

保证数据合法


题解

–长知识了
首先是虚树:只把lca中要用到的有用点拿出来,重新建的一棵树
具体实现主要是按dfs序排序的栈的维护
发现在统计答案的时候就是dfs序相邻的两个点之间的路径上的和
又可以用树状数组维护(差分的思想)
修改也可以O(1)在树状数组上处理了


代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1e5+5;

int n,q;
int head[MAXN],next[MAXN*2],to[MAXN*2],cnt;
long long w[MAXN],s[MAXN];
int fa[MAXN][20],d[MAXN],dfn[MAXN],last[MAXN],sum;
char opt;
int a[MAXN*2],k,tot;
int stack[MAXN],top;
long long ans;

void add(int u,int v){
	cnt++;
	next[cnt]=head[u];
	to[cnt]=v;
	head[u]=cnt;
}

void build(int x,int F){
	dfn[x]=++sum;
	for(int i=1;i<=18;i++)
		fa[x][i]=fa[fa[x][i-1]][i-1];
	for(int i=head[x];i;i=next[i]){
		int y=to[i];
		if(y==F)
			continue;
		d[y]=d[x]+1;
		fa[y][0]=x;
		build(y,x);
	}
	last[x]=sum;
}

void putin(int x,long long y){
	for(x;x<=n;x+=x&-x)
		s[x]+=y;
}

bool comp(const int &a,const int &b){
	return dfn[a]<dfn[b];
}

int lca(int x,int y){
	if(d[x]<d[y])
		swap(x,y);
	for(int i=18;i>=0;i--)
		if(d[fa[x][i]]>=d[y])
			x=fa[x][i];
	if(x==y)
		return x;
	for(int i=18;i>=0;i--)
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];
			y=fa[y][i];
		}
	return fa[x][0];
}

long long ask(int x){
	long long ans=0;
	for(x;x;x-=x&-x)
		ans+=s[x];
	return ans;
}

int main(){
//	freopen("kaihuang.in","r",stdin);
//	freopen("kaihuang.out","w",stdout);
	cin>>n>>q;
	for(int i=1;i<=n;i++)
		scanf("%lld",&w[i]);
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d%d",&u,&v);
		add(u,v);
		add(v,u);
	}
	d[1]=1;
	build(1,0);
	for(int i=1;i<=n;i++){
		putin(dfn[i],w[i]);
		putin(last[i]+1,-w[i]);
	}
	while(q--){
		cin>>opt;
		if(opt=='Q'){
			k=0;
			while(1){
				scanf("%d",&a[++k]);
				if(!a[k]){
					k--;
					break;
				}
			}
			sort(a+1,a+1+k,comp);
			tot=k;
			for(int i=1;i<tot;i++)
				a[++k]=lca(a[i],a[i+1]);
			sort(a+1,a+1+k,comp);
			k=unique(a+1,a+1+k)-a-1;
			ans=0;
			top=0;
			for(int i=1;i<=k;i++){
				while(top&&last[stack[top]]<dfn[a[i]])
					top--;
				if(top)
					ans+=ask(dfn[a[i]])-ask(dfn[stack[top]]);
				else
					ans+=w[a[i]];
				stack[++top]=a[i];
			}
			printf("%lld\n",ans);
		}
		else{
			int x;
			long long W;
			scanf("%d%lld",&x,&W);
			swap(W,w[x]);
			W=w[x]-W;
			putin(dfn[x],W);
			putin(last[x]+1,-W);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41709770/article/details/83153584
今日推荐