【CF856D】Masha and Cactus-樹形DP+LCA+樹狀數組

測試地址:Masha and Cactus
題目大意: 給定一棵樹,再給定mm條邊,每條邊有權值,從裏面選出一些邊加入樹中,使得形成的圖是仙人掌,即每個點至多處在一個環中的圖,並使得加入的邊的權值和最大,求出這個最大值。
做法: 本題需要用到樹形DP+LCA+樹狀數組。
首先轉化一下問題。不難想到,添加一條邊會使得樹上的一條路徑上的點被一個環覆蓋,那麼要使一個點至多在一個環中,也就是要求添加的邊所對應的路徑不相交。那麼問題就轉化爲,從mm條路徑中選出若干不相交的路徑,使得它們的權值和最大。
不難想到用樹形DP選擇此題。令f(v)f(v)爲以vv爲根子樹的答案,那麼vv要麼不被覆蓋,這種情況答案就是它所有兒子的ff值之和,要麼vv就被某條路徑覆蓋,而因爲我們要算的是以vv爲根子樹的答案,所以我們只需考慮LCA爲vv的路徑即可。這樣的話每條路徑會在其兩個端點的LCA處被處理,直接在DP前預處理出來即可。在選擇了某一條路徑後,要使得答案最大,就是要求在子樹中把該路徑所有點刪掉,剩下的所有子樹都達到最大,也就是求子樹中和這條路徑直接相鄰的點的ff值之和。
怎麼求這個東西呢?畫一畫圖可以觀察到,對路徑上每個點,我們把它所有兒子的ff值之和累計,那麼最後多算出來的貢獻就是除了點vv,其餘在路徑上的點的ff值之和,我們把這個多算的部分減去就行了。再稍微調整一下式子,令s(v)s(v)爲點vv所有兒子的ff值之和,減去點vvff值,並令當前路徑爲(x,y)(x,y),那麼答案就可以表示爲,點vv的所有兒子ff值之和,加上vvxx路徑上所有點的ss值之和(不包括點vv),再加上vvyy路徑上所有點的ss值之和(不包括點vv)。這樣我們其實就是求這樣一個問題:每個點有點權,詢問這一點到某點vv的路徑上的點權和。因爲DP的過程就是自下而上的,因此每當我們處理完一個點vv,它的點權就會爲它子樹中所有點的貢獻增加s(v)s(v),在DFS序上就是一個區間加、單點詢問的問題,顯然差分後用樹狀數組處理即可,那麼我們就解決了這一題,時間複雜度爲O(nlogn)O(n\log n)
以下是本人代碼:

#include <bits/stdc++.h>
using namespace std;
int n,m,fa[200010][20]={0},dep[200010],first[200010]={0},tot=0;
int in[200010],out[200010],tim=0,a[200010],b[200010],val[200010];
int sum[200010]={0},f[200010];
vector<int> p[200010];
struct edge
{
	int v,next;
}e[200010];

void insert(int a,int b)
{
	e[++tot].v=b;
	e[tot].next=first[a];
	first[a]=tot;
}

void dfs(int v)
{
	in[v]=++tim;
	for(int i=first[v];i;i=e[i].next)
	{
		dep[e[i].v]=dep[v]+1;
		dfs(e[i].v);
	}
	out[v]=tim;
}

int lca(int a,int b)
{
	if (dep[a]<dep[b]) swap(a,b);
	for(int i=18;i>=0;i--)
		if (dep[fa[a][i]]>=dep[b])
			a=fa[a][i];
	if (a==b) return a;
	for(int i=18;i>=0;i--)
		if (fa[a][i]!=fa[b][i])
			a=fa[a][i],b=fa[b][i];
	return fa[a][0];
}

int lowbit(int x)
{
	return x&(-x);
}

void add(int x,int c)
{
	for(int i=x;i<=n;i+=lowbit(i))
		sum[i]+=c;
}

int calc_sum(int x)
{
	int ans=0;
	for(int i=x;i;i-=lowbit(i))
		ans+=sum[i];
	return ans;
}

void dp(int v)
{
	int downsum=0;
	for(int i=first[v];i;i=e[i].next)
	{
		dp(e[i].v);
		downsum+=f[e[i].v];
	}
	f[v]=downsum;
	for(int i=0;i<p[v].size();i++)
	{
		int x=a[p[v][i]],y=b[p[v][i]],V=val[p[v][i]];
		f[v]=max(f[v],downsum+calc_sum(in[x])+calc_sum(in[y])+V);
	}
	add(in[v],downsum-f[v]);
	add(out[v]+1,f[v]-downsum);
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i=2;i<=n;i++)
	{
		scanf("%d",&fa[i][0]);
		insert(fa[i][0],i);
	}
	dep[0]=-1,dep[1]=0;
	dfs(1);
	for(int i=1;i<=18;i++)
		for(int j=1;j<=n;j++)
			fa[j][i]=fa[fa[j][i-1]][i-1];
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&a[i],&b[i],&val[i]);
		p[lca(a[i],b[i])].push_back(i);
	}
	
	dp(1);
	printf("%d",f[1]);
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章