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
保证数据合法
分析:
裸的树剖
难点在于我们如何快速的处理询问。在询问时我们将询问的点按照树剖的dfs序排序,每次讲相邻两点(第一个点和最后一个点也算相邻的两点)的路径上的和加入ans。每条路径都会被统计两次,ans/2即为最后答案。但相邻两点的lca会被统计不止两次,我们可以将这些lca单独拿出来,最后再加入答案。
code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
#define lowbit(x) (x&-x)
using namespace std;
const LL N=1e5+10;
LL n,m,b[N],last[N],size[N],v[N],deep[N],top[N],fa[N],f[N*4],tr1[N*4],tag[N*4],ans,T,Z,zl[N];
bool bz[N];
struct node{
LL a,b;
node(){}
node(LL x,LL y) {a=y,b=last[x];}
}a[N*2],c[N];
void add(LL x,LL y) {
a[++a[0].a]=node(x,y);last[x]=a[0].a;
}
void change(LL x,LL y) {
LL i=x;
while (i<=n) {
f[i]+=y;
i+=lowbit(i);
}
}
LL sum(LL x) {
LL y=0;
while (x>0) y+=f[x],x-=lowbit(x);
return y;
}
void dfs1(LL x,LL y) {
LL i=last[x];
fa[x]=y;
for (LL i=last[x];i;i=a[i].b) {
if (a[i].a!=y) {
deep[a[i].a]=deep[x]+1;
dfs1(a[i].a,x);
size[x]+=size[a[i].a];
}
}
size[x]++;
}
void dfs2(LL x,LL y) {
LL i,z=0;
v[x]=++T;
for (i=last[x];i;i=a[i].b) {
if (a[i].a!=y) {
if (size[z]<=size[a[i].a]) z=a[i].a;
}
}
if (z!=0) {
top[z]=top[x];
dfs2(z,x);
}
for (i=last[x];i;i=a[i].b) {
if (a[i].a!=y && a[i].a!=z) {
top[a[i].a]=a[i].a;
dfs2(a[i].a,x);
}
}
}
LL lca(LL x,LL y) {
LL z1=x,z2=y;
while (top[x]!=top[y]) {
if (deep[top[x]]<deep[top[y]]) swap(x,y);
ans+=sum(v[x])-sum(v[top[x]]-1);
x=fa[top[x]];
}
if (deep[x]<deep[y]) swap(x,y);
ans+=sum(v[x])-sum(v[y]);
return y;
}
LL lca1(LL x,LL y) {
LL z1=x,z2=y;
while (top[x]!=top[y]) {
if (deep[top[x]]<deep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if (deep[x]<deep[y]) swap(x,y);
if (!bz[y]) {
Z+=b[y],bz[y]=true,zl[++zl[0]]=y;
change(v[y],-b[y]);
}
return y;
}
bool cmp(node a,node b) {
return a.b<b.b;
}
int main() {
// freopen("kaihuang.in","r",stdin);
// freopen("kaihuang.out","w",stdout);
LL i,j,k;
memset(tag,-1,sizeof(tag));
scanf("%lld%lld",&n,&m);
for (i=1;i<=n;i++) scanf("%lld",&b[i]);
for (i=1;i<=n-1;i++) {
LL x,y;scanf("%lld%lld",&x,&y);
add(x,y);add(y,x);
}
dfs1(1,0);
top[1]=1;
dfs2(1,0);
for (i=1;i<=n;i++) change(v[i],b[i]);
for (i=1;i<=m;i++) {
scanf("\n");
char ch=getchar();
if (ch=='Q') {
LL x;scanf("%lld",&x);
c[0].a=0;
while (x) c[++c[0].a].a=x,c[c[0].a].b=v[x],scanf("%lld",&x);
if (c[0].a==1) {
printf("%lld\n",b[c[1].a]);
continue;
}
sort(c+1,c+c[0].a+1,cmp);
c[++c[0].a].a=c[1].a;
ans=Z=zl[0]=0;
for (j=1;j<=c[0].a-1;j++) lca1(c[j].a,c[j+1].a);
for (j=1;j<=c[0].a-1;j++) lca(c[j].a,c[j+1].a);
printf("%lld\n",(ans+Z*2)/2);
for (j=1;j<=zl[0];j++) {
bz[zl[j]]=false;
change(v[zl[j]],b[zl[j]]);
}
}
else {
LL x,y;scanf("%lld%lld",&x,&y);
change(v[x],y-b[x]);
b[x]=y;
}
}
}