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