2018年9月15日提高組模擬賽 T2 拆網線
阿新 • • 發佈:2018-12-10
大意
給定一張個點,條邊的無向聯通圖,現要在圖中至少有一個由個點組成的聯通分量中的點數必須不小於2的情況下,割去儘量多的邊。
思路
樹形
一條邊可以用兩隻企鵝站,這樣的一條點對,越多越好。 如果是對點,,那麼只需要 條邊。 否則,需要條邊。 現在問題就轉為求這樣的點對有多少。
設表示以為根的子樹中能夠兩兩配對的最大點數,不包含節點
設表示以為根的子樹中能夠兩兩配對的最大點數,包含節點
得到方程
程式碼
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;int n,m,t,f[100001],g[100001],ans;
vector<int>son[100001];
bool vis[100001];
inline int read()//輸入優化
{
int f=0,d=1;char c;
while(c=getchar(),c<48||c>57)if(c=='-')d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),c>47&&c<58)f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline void write(register int x){if(x>9)write(x/10);putchar(x%10+48);return ;}//輸出優化
inline void dfs(register int x,register int fa)//樹形dp
{
if(vis[x]) return;
vis[x]=true;//記得標記
f[x]=0;g[x]=0;//初始化
int sum=0;
for(register int i=0;i<son[x].size();i++)
{
int y=son[x][i];
if(y==fa) continue;
dfs(y,x);
g[x]+=max(f[y],g[y]);//動態轉移
sum+=f[y];//計算cgm f[y]
}
for(register int i=0;i<son[x].size();i++)
{
int y=son[x][i];
if(y==fa) continue;
f[x]=max(f[x],sum-f[y]+g[y]+1);//動態轉移
}
return;
}
signed main()
{
t=read();
while(t--)
{
n=read();m=read();
for(register int i=0;i<=n;i++)son[i].clear();
memset(vis,0,sizeof(vis));//初始化
for(register int i=1,u;i<n;i++)
son[u=read()].push_back(i+1),son[i+1].push_back(u);//建邊
dfs(1,-1);
ans=max(f[1],g[1]);//得出點對
if((ans<<1)>=m) write((m+1)>>1),putchar(10);
else write(ans+m-(ans<<1)),putchar(10);//輸出
}
}