1. 程式人生 > >jzoj 5908. 【NOIP2018模擬10.16】開荒(kaihuang)

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