题目
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;
}