【JZOJ A組】開荒(kaihuang)
阿新 • • 發佈:2018-11-08
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 序排序,將相鄰兩個點(包括第一個和最後一個)上的路徑和除以二。
這個可以用樹鏈剖分維護
程式碼
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> using namespace std; const int N=2e6+77; int n,Q,tot,num,cnt,p,w,list[N],v[N],be[N],en[N],d[N],dep[N],f[N][20],k[N],q[N]; long long sz[N],ans; struct edge {int to,next;}e[N]; void dfs(int x,int fa) { f[x][0]=fa,be[x]=++tot,d[x]=d[fa]+1,dep[x]=dep[fa]+1; for (int i=list[x];i;i=e[i].next) if (e[i].to!=fa) dfs(e[i].to,x); en[x]=tot; } bool cmp(int x,int y) { return be[x]<be[y]; } int getlca(int x,int y) { if (dep[x]<dep[y]) swap(x,y); for (int i=18;i>=0;i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i]; if (x==y) return x; for (int i=18;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } void ins(int x,int y) { for (;x<=n;x+=x&-x) sz[x]+=y; } long long getsum(int x) { long long v=0; for (;x;x-=x&-x) v+=sz[x]; return v; } void add(int x,int y) { e[++cnt].to=y; e[cnt].next=list[x]; list[x]=cnt; } int main() { // freopen("kaihuang.in","r",stdin),freopen("kaihuang.out","w",stdout); scanf("%d%d",&n,&Q); for (int i=1;i<=n;i++) scanf("%d",&v[i]); for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); dfs(1,0); for (int j=1;j<=19;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(be[i],v[i]),ins(en[i]+1,-v[i]); while (Q--) { scanf("\n"); char ch=getchar(); int x,y; if (ch=='C') { scanf("%d%d",&x,&y),y-=v[x],v[x]+=y; ins(be[x],y),ins(en[x]+1,-y); } else { num=1,p=0; scanf("%d",&k[num]); while (k[num]!=0) scanf("%d",&k[++num]); num--; sort(k+1,k+num+1,cmp); w=num; for (int i=1;i<=w-1;i++) k[++num]=getlca(k[i],k[i+1]); sort(k+1,k+num+1,cmp),num=unique(k+1,k+num+1)-k-1; ans=0; for (int i=1;i<=num;i++) { for (;p&&en[q[p]]<be[k[i]];) p--; ans+=p?getsum(be[k[i]])-getsum(be[q[p]]):v[k[i]]; q[++p]=k[i]; } printf("%lld\n",ans); } } }