jzoj 5908. 【NOIP2018模擬10.16】開荒(kaihuang)
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;
}
}
}