[虚树][树状数组][lca] Jzoj P5908 开荒
生活随笔
收集整理的這篇文章主要介紹了
[虚树][树状数组][lca] Jzoj P5908 开荒
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
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 0Sample 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=17Data Constraint
數據范圍:20%的數據 n<=10000,q<=500;
另外 20%的數據 k=2
另外 20%的數據 沒有修改操作
所有數據 n,q<=100000,所有詢問 k 的和<=1000000
保證數據合法
?
?
題解
- 題目大意,有q次操作,詢問操作是求k個點兩兩到lca所經過點的點權和,修改操作就是修改一個點的點權
- 首先,求距離可以用樹狀數組按照dfs序加入,類似差分約束一樣求
- 當然修改也可以
- 那么現在問題就是直接暴力兩兩lca做顯然不行,要找一種更快的做法
- 可以建一棵虛樹
- 然后對于虛樹上的非父親節點,直接求出他到父親的點權和(不包括父親),而對于父親則直接加上自己的點權即可
代碼
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 #include <cstring> 5 using namespace std; 6 const int N=2e5+10; 7 int n,Q,tot,num,cnt,p,w,head[N],v[N],be[N],en[N],fa[N],d[N],dep[N],f[N][20],k[N],q[N]; 8 long long sz[N],ans; 9 struct edge {int to,from;}e[N]; 10 void dfs(int x,int pre) 11 { 12 f[x][0]=fa[x]=pre,be[x]=++tot,d[x]=d[fa[x]]+1,dep[x]=dep[pre]+1; 13 for (int i=head[x];i;i=e[i].from) if (e[i].to!=pre) dfs(e[i].to,x); 14 en[x]=tot; 15 } 16 bool cmp(int x,int y) { return be[x]<be[y]; } 17 int getlca(int x,int y) 18 { 19 if (dep[x]<dep[y]) swap(x,y); 20 for (int i=18;i>=0;i--) if (dep[f[x][i]]>=dep[y]) x=f[x][i]; 21 if (x==y) return x; 22 for (int i=18;i>=0;i--) if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; 23 return f[x][0]; 24 } 25 void add(int x,int y) { for (;x<=n;x+=x&-x) sz[x]+=y; } 26 long long getsum(int x) 27 { 28 long long v=0; 29 for (;x;x-=x&-x) v+=sz[x]; 30 return v; 31 } 32 void insert(int x,int y) { e[++cnt].to=y; e[cnt].from=head[x]; head[x]=cnt; } 33 int main() 34 { 35 //freopen("kaihuang.in","r",stdin),freopen("kaihuang.out","w",stdout); 36 scanf("%d%d",&n,&Q); 37 for (int i=1;i<=n;i++) scanf("%d",&v[i]); 38 for (int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),insert(x,y),insert(y,x); 39 dfs(1,0); 40 for (int j=1;j<=19;j++) for (int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1]; 41 for (int i=1;i<=n;i++) add(be[i],v[i]),add(en[i]+1,-v[i]); 42 while (Q--) 43 { 44 scanf("\n"); 45 char ch=getchar(); int x,y; 46 if (ch=='C') 47 { 48 scanf("%d%d",&x,&y),y-=v[x],v[x]+=y; 49 add(be[x],y),add(en[x]+1,-y); 50 } 51 else 52 { 53 num=1,p=0; scanf("%d",&k[num]); while (k[num]!=0) scanf("%d",&k[++num]); num--; 54 sort(k+1,k+num+1,cmp); 55 w=num; for (int i=1;i<=w-1;i++) k[++num]=getlca(k[i],k[i+1]); 56 sort(k+1,k+num+1,cmp),num=unique(k+1,k+num+1)-k-1; 57 ans=0; 58 for (int i=1;i<=num;i++) 59 { 60 for (;p&&en[q[p]]<be[k[i]];) p--; 61 ans+=p?getsum(be[k[i]])-getsum(be[q[p]]):v[k[i]]; 62 q[++p]=k[i]; 63 } 64 printf("%lld\n",ans); 65 } 66 } 67 }?
轉載于:https://www.cnblogs.com/Comfortable/p/9802591.html
總結
以上是生活随笔為你收集整理的[虚树][树状数组][lca] Jzoj P5908 开荒的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Luogu1891]疯狂LCM[辗转相
- 下一篇: 关于CDN的部署思路和技术架构