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的方法求。而修改一个点相当于修改区间,然后单点查询,树状数组可以完美解决。然后对于虚树上的非父亲节点,直接求出他到父亲的点权和(不包括父亲),而对于父亲则直接加上自己的点权即可。
代码:
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define LL long long
const int maxn=1e5+7;
using namespace std;
int n,m,x,y,cnt,tot,top;
int ls[maxn],dfn[maxn],last[maxn],f[maxn][20],b[maxn],dep[maxn],q[maxn],pre[maxn],p[maxn];
LL ans,t[maxn],a[maxn],w;
char op[3];
struct edge{
int y,next;
}g[maxn*2];
void add(int x,int y)
{
g[++cnt]=(edge){y,ls[x]};
ls[x]=cnt;
}
void dfs(int x,int fa)
{
dfn[x]=last[x]=++cnt;
f[x][0]=fa;
dep[x]=dep[fa]+1;
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
if (y==fa) continue;
dfs(y,x);
last[x]=last[y];
}
}
bool cmp(int a,int b)
{
return dfn[a]<dfn[b];
}
void updata(int x,LL k)
{
for (int i=x;i<=n;i+=i&(-i)) t[i]+=k;
}
LL getsum(int x)
{
LL sum=0;
for (int i=x;i>0;i-=i&(-i)) sum+=t[i];
return sum;
}
void ins(int l,int r,LL k)
{
updata(l,k);
if (r!=n) updata(r+1,-k);
}
int getlca(int x,int y)
{
if (dep[x]>dep[y]) swap(x,y);
int d=dep[y]-dep[x],k=19,t=1<<k;
while (d)
{
if (d>=t) d-=t,y=f[y][k];
t/=2,k--;
}
if (x==y) return x;
k=19;
while (k>=0)
{
if (f[x][k]!=f[y][k])
{
x=f[x][k];
y=f[y][k];
}
k--;
}
return f[x][0];
}
LL getdis(int x,int y)
{
return getsum(dfn[y])-getsum(dfn[x]);
}
int main()
{
freopen("kaihuang.in","r",stdin);
freopen("kaihuang.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1;i<n;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
cnt=0;
dfs(1,0);
for (int j=1;j<20;j++)
{
for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
}
for (int i=1;i<=n;i++) ins(dfn[i],last[i],a[i]);
for (int i=1;i<=m;i++)
{
scanf("%s",op);
if (op[0]=='Q')
{
scanf("%d",&x);
tot=0;
while (x)
{
b[++tot]=x;
scanf("%d",&x);
}
sort(b+1,b+tot+1,cmp);
top=0,cnt=0;
q[++top]=b[1];
p[++cnt]=b[1];
for (int j=2;j<=tot;j++)
{
p[++cnt]=b[j];
int d=getlca(q[top],b[j]);
if (d==q[top]) q[++top]=b[j];
else
{
while (dep[q[top-1]]>=dep[d])
{
pre[q[top]]=q[top-1];
top--;
}
if (q[top]!=d)
{
pre[q[top]]=d;
q[top]=d;
p[++cnt]=d;
}
q[++top]=b[j];
}
}
while (top>=1) pre[q[top]]=q[top-1],top--;
ans=0;
for (int i=1;i<=cnt;i++)
{
if (pre[p[i]]) ans+=getdis(pre[p[i]],p[i]);
else ans+=a[p[i]];
}
printf("%lld\n",ans);
}
else
{
scanf("%d%lld",&x,&w);
ins(dfn[x],last[x],w-a[x]);
a[x]=w;
}
}
}