1. 程式人生 > >【NOIP2018模擬賽2018.10.18】開荒

【NOIP2018模擬賽2018.10.18】開荒

題目

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中要用到的有用點拿出來,重新建的一棵樹
具體實現主要是按dfs序排序的棧的維護
發現在統計答案的時候就是dfs序相鄰的兩個點之間的路徑上的和
又可以用樹狀陣列維護(差分的思想)
修改也可以O(1)在樹狀陣列上處理了


程式碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include
<cmath>
#include<cstring> #include<algorithm> using namespace std; const int MAXN=1e5+5; int n,q; int head[MAXN],next[MAXN*2],to[MAXN*2],cnt; long long w[MAXN],s[MAXN]; int fa[MAXN][20],d[MAXN],dfn[MAXN],last[MAXN],sum; char opt; int a[MAXN*2],k,tot; int stack[MAXN],top; long long ans; void add(int u,int v){ cnt++; next[cnt]=head[u]; to[cnt]=v; head[u]=cnt; } void build(int x,int F){ dfn[x]=++sum; for(int i=1;i<=18;i++) fa[x][i]=fa[fa[x][i-1]][i-1]; for(int i=head[x];i;i=next[i]){ int y=to[i]; if(y==F) continue; d[y]=d[x]+1; fa[y][0]=x; build(y,x); } last[x]=sum; } void putin(int x,long long y){ for(x;x<=n;x+=x&-x) s[x]+=y; } bool comp(const int &a,const int &b){ return dfn[a]<dfn[b]; } int lca(int x,int y){ if(d[x]<d[y]) swap(x,y); for(int i=18;i>=0;i--) if(d[fa[x][i]]>=d[y]) x=fa[x][i]; if(x==y) return x; for(int i=18;i>=0;i--) if(fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; } return fa[x][0]; } long long ask(int x){ long long ans=0; for(x;x;x-=x&-x) ans+=s[x]; return ans; } int main(){ // freopen("kaihuang.in","r",stdin); // freopen("kaihuang.out","w",stdout); cin>>n>>q; for(int i=1;i<=n;i++) scanf("%lld",&w[i]); for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } d[1]=1; build(1,0); for(int i=1;i<=n;i++){ putin(dfn[i],w[i]); putin(last[i]+1,-w[i]); } while(q--){ cin>>opt; if(opt=='Q'){ k=0; while(1){ scanf("%d",&a[++k]); if(!a[k]){ k--; break; } } sort(a+1,a+1+k,comp); tot=k; for(int i=1;i<tot;i++) a[++k]=lca(a[i],a[i+1]); sort(a+1,a+1+k,comp); k=unique(a+1,a+1+k)-a-1; ans=0; top=0; for(int i=1;i<=k;i++){ while(top&&last[stack[top]]<dfn[a[i]]) top--; if(top) ans+=ask(dfn[a[i]])-ask(dfn[stack[top]]); else ans+=w[a[i]]; stack[++top]=a[i]; } printf("%lld\n",ans); } else{ int x; long long W; scanf("%d%lld",&x,&W); swap(W,w[x]); W=w[x]-W; putin(dfn[x],W); putin(last[x]+1,-W); } } return 0; }