題意:
有一棵棵提米樹,滿足這樣的性質:
每個點上長了一定數量的Temmie 薄片,薄片數量記爲這個點的權值,這些點被標記爲 1 到 n 的整數,其
中 1 號點是樹的根,沒有孩子的點是樹上的葉子。
定義\((a,b)\)是一對相鄰的葉子,當且僅當沒有其它的葉子節點在 DFS 序上在a,b 之間。
每對相鄰的葉子都會產生一個代價,代價爲 a 到 b 路徑上(不包含 a,b)的點中,最大點權值。
提米樹可以提供決心,一棵提米樹能提供的決心的數量是樹上所有葉子上長的 Temmie 薄片數量和,減去所有相鄰葉子的代價。
Temmie 們決定對這棵樹進行若干次剪枝(可以不剪枝),使得這棵樹能提供的決心最多。
一次剪枝定義爲:如果一個點的孩子都是葉子,就可以把它所有的孩子剪掉。
要求\(O(n)\)做法。
首先,考慮\(O(n^2)\)的60分暴力:
我們可以反過來:由根開始,每個節點考慮是否擴展出所有葉子。
若不擴展,則它的子樹都是空的,它成爲葉子。
我們可以在dfs序上DP:設\(dp(i,j)\)表示考慮到i,上一個葉子點爲j的最大決心。
有兩種轉移:
1、若i不是葉子,可以擴展,轉移到\(dp(i+1,j)\)。
2、可以不擴展,轉移到\(dp(i+si_i,i)+w_i-max(i,j)\)。
代碼如下:
for(int i=tm-1;i>=0;i--)
{
for(int j=0;j<=n;j++)
{
int u=xl[i];
dp[i][j]=dp[i+si[u]][u]+sz[u]-zd[j][u];
if(si[u]>1&&dp[i+1][j]>dp[i][j])
dp[i][j]=dp[i+1][j];
}
}
printf("%d",dp[0][0]);
考慮優化:首先,要把維度降下來。
設\(dp(i)\)表示i成爲葉子後的最大決心。
枚舉下一個使用2轉移的位置,代碼如下:
for(int i=tm;i>=0;i--)
{
int u=xl[i];
dp[i]=-999999999;
for(int j=i+si[u];j<=tm+1;j++)
{
int t=dp[j]-zd[u][xl[j]];
if(t>=dp[i])
dp[i]=t;
if(si[xl[j]]==1)//注意此處,非常關鍵。
break;
}
dp[i]+=sz[u];
}
printf("%d",dp[0]);
繼續優化:
我們發現,對於\(i\),\(i+si_i\)就是i的祖先節點中第一個有更右子節點的點。
而由於註釋處的break,使得轉移就是在\(i+si[u]\)處,一直向左走形成的鏈。
那麼:
綠點對橙點有貢獻。
那麼,我們枚舉紅點lca,再枚舉相鄰的兩個兒子,計算貢獻。
先算出鏈上每個節點到lca的最大值。設爲\(h\),那麼,就是\(dp(u)=max(dp(v)-max(h(u),h(v)))+w(u)\)。
由於h具有單調性,因此分\(h(u)>h(v)\)和\(h(u)<=h(v)\)進行討論,提前算出鏈上\(dp\),以及\(dp-h\)的最大值。
這兩種情況符合的v一定是前綴/後綴,雙指針掃一下即可定位。
代碼細節非常多。
#include <stdio.h>
#include <vector>
#define inf 999999999
using namespace std;
vector<int> ve[100010];
int sz[100010],cl[100010],cr[100010],dp[100010],zd[100010],fa[100010],ma[100010],md[100010];
int max(int a,int b)
{
return a>b?a:b;
}
void dfs0(int u,int f)
{
fa[u]=f;
for(int i=0;i<ve[u].size();i++)
dfs0(ve[u][i],u);
}
void dfs1(int u)
{
for(int i=ve[u].size()-2;i>=0;i--)
{
dfs1(ve[u][i+1]);
int t=ve[u][i],la=0;zd[u]=sz[u];
while(t!=0)
{
zd[t]=max(sz[t],zd[fa[t]]);
t=cr[t];
}
t=ve[u][i+1];
while(t!=0)
{
zd[t]=max(sz[t],zd[fa[t]]);
la=t;t=cl[t];
}
t=la;while(t!=u)
{
ma[t]=dp[t]-zd[fa[t]];
if(cl[t])ma[t]=max(ma[t],ma[cl[t]]);
t=fa[t];
}
t=ve[u][i+1];
while(t!=0)
{
md[t]=dp[t];
if(fa[t]!=u)md[t]=max(md[t],md[fa[t]]);
t=cl[t];
}
ma[u]=md[u]=-inf;
int x=ve[u][i],y=ve[u][i+1];
while(x!=0)
{
while(y!=0&&zd[fa[y]]<zd[fa[x]])
y=cl[y];
if(y)
{
t=ma[y]+sz[x];
if(t>dp[x])dp[x]=t;
}
t=md[(y==0?la:fa[y])]-zd[fa[x]]+sz[x];
if(t>dp[x])dp[x]=t;x=cr[x];
}
}
if(ve[u].size())dfs1(cl[u]);
}
int main()
{
freopen("temmie.in","r",stdin);
freopen("temmie.out","w",stdout);
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int s,a;
scanf("%d%d",&sz[i],&s);
for(int j=0;j<s;j++)
{
scanf("%d",&a);
ve[i].push_back(a);
}
if(s>0)
{
cl[i]=ve[i][0];
cr[i]=ve[i][s-1];
}
dp[i]=-inf;
}
int u=1;
while(u!=0)
{
dp[u]=sz[u];
u=cr[u];
}
dfs0(1,0);dfs1(1);
int ma=-inf;u=1;
while(u!=0)
{
if(dp[u]>ma)
ma=dp[u];
u=cl[u];
}
printf("%d",ma);
return 0;
}