摧毁树状图
有一颗 n 个节点的树,可以将树上的两条链(边不得共用,点可以)上的节点删除,问删除后最多能得到最多的连通块。
n≤105
吐槽
这道题可以说是树上动规的集大成之题目,细节部分实在很容易考虑不全面,不过出题人还是很良心的,给了大力搜索
60
, 打表提答
80
.
这种神题我怎么可能做出来,都是考完后照着大神的题解改的,不过我感觉我把代码加了些注释应该更明了一些。
预备
首先,当我们开心地敲完
80
分的暴力后,我们容易看出这是一道的树上动规,然后我们就要设计状态了。
在设计之前呢,我们首先要考虑一条链的情况:
设
d(u)
为点
u
的儿子数量
状态设计
经过巧(wu)妙(nao)的设计,我们对一个节点记录一下状态:
- f(u,0) :以 u 为根的子树,有一条可向上延伸的链的最大答案;
f(u,1) :……,不含 u 的完整链的最大答案;f(u,2) :……,含 u ……f(u,3) :……,一条完整的链、一条可向上延伸的链的最大答案;- f(u,4) :……,不含 u 的完整的两条链的最大答案;
f(u,5) :……,含 u ……
状态转移
状态设计一时爽,状态转移火葬场
嘛,总之状态的转移很繁琐就是了,我真的没有信心在考试的时候考虑到所有的情况(可能是我太弱了),情况非常多,代码里都注释上来,搭配一下内容应该很容易食用了。
f(u,0)
- 从儿子转移上来
- 另起炉灶
-
f(u,1)
- 从儿子转移上来
- f(v,2)+1
-
f(u,2)
- 选两个 f(v,0) 拼起来
-
f(u,3)
- 从儿子转移上来
- 完整的链 + f(v,0)
- 三条 f(v,0)
-
f(u,4)
- 同一儿子转移上来
- 不同儿子……
- 多种情况,注意 ±1
-
f(u,5)
- 两个 f(u,3) 拼起来
- 四条 f(v,0)
- 从一个儿子转移上来一整条链,然后选两条 f(v,0) 拼一起
具体实现的时候注意 mx 的初值可能为 0 或
−∞ ,这取决于 mx 所代表的值是否可以不选,具体见代码。
/**************************************************************
Problem: 4871
User: zhangche0526
Language: C++
Result: Accepted
Time:2936 ms
Memory:7932 kb
****************************************************************/
#include<iostream>
#include<cstdio>
#include<cstring>
typedef long long ll;
int read();
const int MAXN=1e5+5,INF=0x0f0f0f0f;
int n;
struct E{int next,to;} e[MAXN<<1];int ecnt,G[MAXN];
void addEdge(int u,int v){e[++ecnt]=(E){G[u],v};G[u]=ecnt;}
void addEdge2(int u,int v){addEdge(u,v);addEdge(v,u);}
int f[MAXN][6],scnt[MAXN];//scnt is actually sonNumber - 1;
void calScnt(int u,int la)
{
scnt[u]=-1;
for(int i=G[u];i;i=e[i].next)
if(e[i].to!=la) ++scnt[u],calScnt(e[i].to,u);
}
inline void upd(int &x,const int &y){if(y>x) x=y;}
void dp(int u,int la)
{
memset(f[u],-0x3f,sizeof(f[u]));
int i;
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
dp(e[i].to,u);
//f(u,0)
//{
f[u][0]=scnt[u];
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
upd(f[u][0],f[e[i].to][0]+scnt[u]);
//}
//f(u,1)
//{
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
upd(f[u][1],f[e[i].to][2]+1),
upd(f[u][1],f[e[i].to][1]);
//}
//f(u,2)
//{
int mx1=0;
f[u][2]=scnt[u]+1;
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
upd(f[u][2],mx1+f[e[i].to][0]+scnt[u]+1),
upd(mx1,f[e[i].to][0]);
//}
//f(u,3)
//{
// case 1
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
upd(f[u][3],f[e[i].to][3]+scnt[u]);
// case 2
int mx2=0;mx1=-INF;
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
{
int v=e[i].to;
upd(f[u][3],mx1+f[v][0]+scnt[u]);
upd(f[u][3],f[v][1]-1+mx2+scnt[u]);
upd(f[u][3],f[v][2]-1+mx2+scnt[u]);
upd(mx1,f[v][1]-1),upd(mx1,f[v][2]-1);
upd(mx2,f[v][0]);
}
// case 3
int mx3=0;mx1=mx2=0;
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
{
int val=f[e[i].to][0];
if(val>mx1) std::swap(mx1,val);
if(val>mx2) std::swap(mx2,val);
if(val>mx3) std::swap(mx3,val);
}
upd(f[u][3],mx1+mx2+mx3+scnt[u]);
//}
//f(u,4)
//{
// case 1
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
upd(f[u][4],f[e[i].to][5]+1),
upd(f[u][4],f[e[i].to][4]);
// case2
mx1=-INF,mx2=-INF;
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
{
int v=e[i].to;
upd(f[u][4],f[v][1]+mx1-1);
upd(f[u][4],f[v][1]+mx2);
upd(f[u][4],f[v][2]+mx1);
upd(f[u][4],f[v][2]+mx2+1);
upd(mx1,f[v][1]),upd(mx2,f[v][2]);
}
//}
//f(u,5)
//{
// case 1
mx1=-INF,mx2=0;
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
{
upd(f[u][5],mx1+f[e[i].to][0]+scnt[u]+1);
upd(f[u][5],mx2+f[e[i].to][3]+scnt[u]+1);
upd(mx1,f[e[i].to][3]),upd(mx2,f[e[i].to][0]);
}
// case 2
int mx4=0;mx1=mx2=mx3=0;
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
{
int val=f[e[i].to][0];
if(val>mx1) std::swap(mx1,val);
if(val>mx2) std::swap(mx2,val);
if(val>mx3) std::swap(mx3,val);
if(val>mx4) std::swap(mx4,val);
}
upd(f[u][5],mx1+mx2+mx3+mx4+scnt[u]+1);
// case 3
static int sons[MAXN];int sz=0;
sons[0]=-1;
for(i=G[u];i;i=e[i].next) if(e[i].to!=la)
sons[++sz]=e[i].to;
static int preSgMx[MAXN],preDbMx[MAXN],sufSgMx[MAXN],sufDbMx[MAXN];
preSgMx[0]=preDbMx[0]=sufSgMx[sz+1]=sufDbMx[sz+1]=0;
for(i=1;i<=sz;i++)
preDbMx[i]=std::max(preDbMx[i-1],f[sons[i]][0]+preSgMx[i-1]),
preSgMx[i]=std::max(preSgMx[i-1],f[sons[i]][0]);
for(i=sz;i;i--)
sufDbMx[i]=std::max(sufDbMx[i+1],f[sons[i]][0]+sufSgMx[i+1]),
sufSgMx[i]=std::max(sufSgMx[i+1],f[sons[i]][0]);
for(i=1;i<=sz;i++)
{
int mx=std::max(f[sons[i]][1],f[sons[i]][2]);
upd(f[u][5],mx+preDbMx[i-1]+scnt[u]);
upd(f[u][5],mx+sufDbMx[i+1]+scnt[u]);
upd(f[u][5],mx+preSgMx[i-1]+sufSgMx[i+1]+scnt[u]);
}
//}
}
int main()
{
int T=read(),x=read();
while(T--)
{
int i;
n=read();
if(x>=1) read(),read();
if(x==2) read(),read();
ecnt=0;memset(G,0,sizeof(G));
for(i=1;i<n;i++)
{
int u=read()-1,v=read()-1;
addEdge2(u,v);
}
calScnt(0,-1);
dp(0,-1);
printf("%d\n",std::max(f[0][4],f[0][5]));
}
return 0;
}
int read()
{
char c;int flag=1,res=0;
do c=getchar();while(c!='-'&&(c<'0'||c>'9'));
if(c=='-') flag=-1;else res=c-'0';c=getchar();
while(c>='0'&&c<='9'){res=res*10+c-'0';c=getchar();}
return flag*res;
}