有一颗 n 个节点的树,可以将树上的两条链(边不得共用,点可以)上的节点删除,问删除后最多能得到最多的连通块。
n≤105
吐槽
这道题可以说是树上动规的集大成之题目,细节部分实在很容易考虑不全面,不过出题人还是很良心的,给了大力搜索 60 , 打表提答 80 .
这种神题我怎么可能做出来,都是考完后照着大神的题解改的,不过我感觉我把代码加了些注释应该更明了一些。
预备
首先,当我们开心地敲完 80 分的暴力后,我们容易看出这是一道的树上动规,然后我们就要设计状态了。
在设计之前呢,我们首先要考虑一条链的情况:
设 d(u) 为点 u 的儿子数量 −1 ,那么删除一条链的剩下的联通块个数为 1+∑ud(u) ,如果这条链两个端点的 LCA 不为根,那么答案还需要 +1 ,因为上方还有一个联通块。
状态设计
经过巧(wu)妙(nao)的设计,我们对一个节点记录一下状态:
状态转移
状态设计一时爽,状态转移火葬场
嘛,总之状态的转移很繁琐就是了,我真的没有信心在考试的时候考虑到所有的情况(可能是我太弱了),情况非常多,代码里都注释上来,搭配一下内容应该很容易食用了。
具体实现的时候注意 mx 的初值可能为 0 或 −∞ ,这取决于 mx 所代表的值是否可以不选,具体见代码。
/**************************************************************
Problem: 4871
User: zhangche0526
Language: C++
Result: Accepted
Time:2936 ms
Memory:7932 kb
****************************************************************/
#include
#include
#include
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;iint 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;
}