CF1776M Parmigiana With Seafood 题解
先将所有的叶子取 \(\max\) 贡献给答案,以下讨论的所有点中不考虑叶子。
首先可以考虑先手能否删到 \(n\):不难发现当 \(2 | n\) 的时候可以,然后我们就排除了一半的 \(n\),于是以下令 \(2 \not | n\)。接下来,考虑先手能否删掉 \(n-1\),那么把 \(n-1 \to n\) 的路径当成一个大点,容易发现当 \(2\not | \text{dis}_{n-1,n}\) 的时候才能删掉 \(n-1\)。也就相当于对于任意二元组 \((x,y)\) ,满足 \(2\not|\text{dis}_{x,y}\),\(\min\{x,y\}\) 必定可以贡献给答案。
类似地,我们希望找到其他形式的一些极小的点集 \(S\),使得 \(\min\limits_{x \in S}x\) 可以贡献给答案,由于奇数距离点对已经讨论完,接下来讨论两两距离为偶数的点。
考虑两两距离为偶数的点所构成的虚树,考虑三个点构成的虚树。那么这相当于共线的三个点或者满足 \(\exist u,2 | \text dis_{u,x},2 | \text dis_{u,y},2 | \text dis_{u,z}\) 或者 \(\exist u,2 \not | \text dis_{u,x},2 \not| \text dis_{u,y},2 \not| \text dis_{u,z}\) 的三元组 \((x,y,z)\)。考虑后面两者,其中后者没有什么好的性质,考虑前者。
发现这个东西的贡献直接就是 \(\min\{x,y,z\}\),这是因为图上除了这个虚树之外的点的个数为偶数,那么由于它有 \(3\) 个叶子,删剩这个虚树以及叶子上挂的 \(3\) 个点的时候删的步数必定是奇数,那么下一步后手必定会暴露出一个叶子,先手直接删掉即可。
再继续手玩一下,发现不是很能继续拓展了。考虑证明:只需证明对于所有满足集合内任意两点距离为偶数并且任意三点不构成上面的三元组 \((x,y,z)\) 的点集 \(S\),后手可以拿走里面所有的点即可。
这时可以发现,点集 \(S\) 中的点一定是一些相邻两点距离为 \(2\) 的链和 \(\exist u,2 \not | \text dis_{u,x},2 \not| \text dis_{u,y},2 \not| \text dis_{u,z}\) 的三元组 \((x,y,z)\),那么结论的正确性就是显然的。
所以现在的答案就是所有的满足前面条件的二元组 \((x,y)\) 的 \(\min\{x,y\}\) 和三元组 \((x,y,z)\) 的 \(\min\{x,y,z\}\) 的最大值。直接 DP 即可做到线性。
但是通过不知道怎么想到地额外发掘一些性质,存在一个更优美的线性做法:所有的点集必定形如 \((n,x),(n,x,y),(x,y,z)\) ,其中最后一种满足 \(u=n\)。前两种显然,考虑证明最后一种:如果存在一个不满足条件的三元组 \((x,y,z)\) ,那么一定满足 \((n,x),(n,y),(n,z),(x,y),(y,z),(z,x)\) 这几个点对的距离全部为偶数,简单分讨可知 \((n,x,y)\) 必定也是一个满足条件的点集。
所以直接以 \(n\) 为根进行一次 dfs 即可,复杂度 \(O(n)\)。
代码:
const int N=1e5+50;
int n,f[N][2],dep[N],deg[N],head[N],cnt,maxn,maxn1,maxn2,maxn3,ans;
struct edge{int to,nxt;}e[N<<1];
inline void addedge(int u,int v){e[++cnt]=(edge){v,head[u]},head[u]=cnt;}
void dfs(int u,int fa)
{
dep[u]=dep[fa]+1,f[u][0]=(deg[u]>1)?u:0;
if(dep[u]&1) ans=max(ans,u);
else if(deg[u]>1) maxn=max(maxn,u);
int maxn1=0,maxn2=0;
for(int i=head[u];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
dfs(v,u),f[u][0]=max(f[u][0],f[v][1]),f[u][1]=max(f[u][1],f[v][0]);
if(f[v][1]>=maxn1) maxn2=maxn1,maxn1=f[v][1];
else if(f[v][1]>maxn2) maxn2=f[v][1];
}
if(!(dep[u]&1)) ans=max(ans,maxn2);
}
int main(void)
{
n=read();int u,v;
fr(i,2,n) u=read(),v=read(),addedge(u,v),addedge(v,u),++deg[u],++deg[v];
if(!(n&1)) return writeln(n),0;
fr(i,1,n) if(deg[i]==1) ans=max(ans,i);
for(int i=head[n];i;i=e[i].nxt)
{
maxn=0,dfs(e[i].to,n);
if(maxn>=maxn1) maxn3=maxn2,maxn2=maxn1,maxn1=maxn;
else if(maxn>=maxn2) maxn3=maxn2,maxn2=maxn;
else if(maxn>maxn3) maxn3=maxn;
}
return writeln(max(maxn3,ans)),0;
}