1. 程式人生 > >【JZOJ A組】開荒(kaihuang)

【JZOJ A組】開荒(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 序排序,將相鄰兩個點(包括第一個和最後一個)上的路徑和除以二。
這個可以用樹鏈剖分維護

程式碼

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