Codeforces856D Masha and Cactus【樹鏈的兒子之和】

題目描述:

大意:給出樹上的一些帶權值的鏈,選出一些點不相交的鏈,使得它們的權值和最大。n,m2105n,m\le2*10^5

題目分析:

在這裏插入圖片描述
問題就是怎麼求刪掉這條鏈後的極大子樹的DP值。

大力數據結構可以重鏈剖分後在每個點上存下輕兒子的DP值之和,然後往上跳重鏈,每次加上這條重鏈上存的值(單點修改區間求和),減去上一次跳上來的輕兒子,加上當前點的重兒子。複雜度O(nlog2n)O(nlog^2n)

對於這道題還有複雜度更低的做法:記s[x]s[x]表示xx的所有兒子的DP值之和,那麼對於一條以uu爲LCA,鏈端點爲x,yx,y的鏈,它對f[u]f[u]的貢獻就是這條鏈上的ss值之和減去鏈上除了uu點的ff值。
g[x]=s[x]f[x]g[x]=s[x]-f[x],貢獻變爲鏈上除了uu點的gg值之和加上uu的所有兒子的ff值之和。
由於DP是從下往上的,g[u]g[u]一定會在鏈端點在它的子樹中時被統計,所以給uu的子樹直接加上g[u]g[u],查詢時直接查詢鏈端點處的值即可(區間修改單點查詢),複雜度O(nlogn)O(nlogn)

上面這種做法,在最後一步可以將子樹加換成並查集縮鏈並維護權值,複雜度更低,但是在換父親時有一點實現上的細節,詳見這篇博客

Code:

#include<bits/stdc++.h>
#define maxn 200005
using namespace std;
char cb[1<<20],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<20,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
	char c;while(!isdigit(c=getc()));
	for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
const int Log = 17;
int n,m,F[Log+1][maxn],dep[maxn],in[maxn],out[maxn],tim,f[maxn];
int arr[maxn],fir[maxn],nxt[maxn];
inline void line(int x,int y){nxt[y]=fir[x],fir[x]=y;}
struct node{int x,y,w;};
vector<node>q[maxn];
inline int LCA(int u,int v){
	if(dep[u]<dep[v]) swap(u,v);
	for(int i=0,d=dep[u]-dep[v];d;d>>=1,i++) if(d&1) u=F[i][u];
	if(u==v) return u;
	for(int i=Log;i>=0;i--) if(F[i][u]!=F[i][v]) u=F[i][u],v=F[i][v];
	return F[0][u];
}
void dfs(int u){in[u]=++tim;for(int i=fir[u];i;i=nxt[i]) dep[i]=dep[u]+1,dfs(i);out[u]=tim;}
inline void upd(int i,int v){for(;i<=n;i+=i&-i) arr[i]+=v;}
inline int qsum(int i){int s=0;for(;i;i-=i&-i) s+=arr[i];return s;}
int main()
{
	freopen("1.in","r",stdin);
	int x,y,w;
	read(n),read(m);
	for(int i=2;i<=n;i++) read(F[0][i]),line(F[0][i],i);
	for(int j=1;j<=Log;j++)
		for(int i=1;i<=n;i++)
			F[j][i]=F[j-1][F[j-1][i]];
	dfs(1);
	while(m--){
		read(x),read(y),read(w);
		q[LCA(x,y)].push_back((node){x,y,w});
	}
	for(int u=n;u>=1;u--){
		for(node t:q[u]) f[u]=max(f[u],qsum(in[t.x])+qsum(in[t.y])+t.w);
		int s=0;
		for(int v=fir[u];v;v=nxt[v]) s+=f[v];
		f[u]+=s;
		upd(in[u],s-f[u]),upd(out[u]+1,f[u]-s);
	}
	printf("%d\n",f[1]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章