[六省联考2017]摧毁"树状图"
Description:
给定一棵树,要删除两条不相交的链上的所有点,求删除后最大联通块个数
Hint:
\(n \le 10^5\)
Solution:
毒瘤树型dp,设链的dp确实蛮套路,但是这题的转移是谁能想的不重不漏?
详见代码:
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1
#define rs p<<1|1
using namespace std;
typedef long long ll;
const int mxn=2e5+5;
int T,n,m,ans,cnt,opt,in[mxn],hd[mxn],vis[mxn];
int dp[mxn][4];
inline int read() {
char c=getchar(); int x=0,f=1;
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
return x*f;
}
inline void chkmax(int &x,int y) {if(x<y) x=y;}
inline void chkmin(int &x,int y) {if(x>y) x=y;}
struct ed {
int to,nxt;
}t[mxn<<1];
inline void add(int u,int v) {
t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt; ++in[v];
}
// 路径有折返,链表示端点为根的不折返路径
// dp[u][0]表示过根的一条链
// dp[u][1]表示不过根的一条路径
// dp[u][2]表示过根的一条路径
// dp[u][3]表示过根的一条链+一条路径
void dfs(int u) {
dp[u][0]=dp[u][2]=dp[u][3]=in[u]; //删掉该点本身
dp[u][1]=1; vis[u]=1; /*起始状态,只能删叶节点*/ int res=0;
for(int i=hd[u];i;i=t[i].nxt) {
int v=t[i].to;
if(vis[v]) continue ;
dfs(v);
chkmax(ans,dp[u][3]+dp[v][0]-(u==1));
// 由于这两个状态没有算u上方的贡献,故要讨论u==1,下同理
chkmax(ans,dp[u][0]+dp[v][3]-(u==1));
chkmax(ans,dp[u][1]+dp[v][2]);
// 不用加u上方的贡献了,因为dp[u][1]已经包含了
chkmax(ans,dp[u][1]+dp[v][1]-1);
// 必须减,u上方被算了两次
chkmax(ans,dp[u][2]+dp[v][1]-(u==1));
chkmax(ans,dp[u][2]+dp[v][2]-(u==1));
chkmax(dp[u][1],dp[v][1]);
chkmax(dp[u][1],dp[v][2]+1);
// 过根->不过根,联通块+1
chkmax(dp[u][3],dp[u][0]+dp[v][2]-1);
// u多算了v这个联通块,减去,下同理
chkmax(dp[u][3],dp[u][2]+dp[v][0]-1);
chkmax(dp[u][3],dp[v][3]+in[u]-1);
chkmax(dp[u][3],dp[v][0]+in[u]+res-2);
// 比较复杂,但总之是减去算重的块
chkmax(dp[u][3],dp[u][0]+dp[v][1]-1);
chkmax(dp[u][2],dp[u][0]+dp[v][0]-1);
chkmax(dp[u][0],dp[v][0]+in[u]-1);
chkmax(dp[u][2],dp[u][0]);
chkmax(dp[u][3],dp[u][2]);
// 状态的包含关系
chkmax(res,dp[v][1]); chkmax(res,dp[v][2]);
// 更新res
}
} // 注意dp顺序,不要自己把自己算了
int main()
{
T=read(); opt=read(); int u,v;
while(T--) {
n=read(); cnt=ans=0;
for(int i=1;i<=opt*2;++i) u=read();
memset(in,0,sizeof(in));
memset(hd,0,sizeof(hd));
memset(vis,0,sizeof(vis));
for(int i=2;i<=n;++i) {
u=read(); v=read();
add(u,v); add(v,u); --in[i]; // 减去父亲贡献的度数
}
dfs(1); printf("%d\n",ans);
}
return 0;
}
`cpp