AT_arc152_f [ARC152F] Attraction on Tree
好难评的证明。
最后的答案形式是 \(\operatorname{dis}(1,n)+2k\),那么若 \(n\not\equiv\operatorname{dis}(1,n)\) 无解。考虑答案的下界,即 \(1\) 到 \(n\) 的这条链。现在要将点限定在这条链上,方案是钦定 \(\operatorname{dis}(1,n)\) 个点把它拉到 \(n\),然后剩下的点两两匹配在链上左右横跳。需要满足匹配点不在同一个链上点的子树内。
现在暂时不关心子树的形态。令 \(a_x\) 为 \(x\) 这个点不在链上的子树大小。存在合法匹配的一个必要条件是删掉钦定的点后不存在绝对众数。考虑链上的第 \(i\) 个点最多有 \(i\) 个钦定点,即:
\(a_i-i\le \frac{n-\operatorname{dis}(1,n)}{2}\)。不合法即左式大于右式。
现在证明,最多有一个点不满足这个小于等于的限制:假设有 \(i\ne j\) 使得 \(a_j-j>\frac{n-\operatorname{dis}(1,n)}{2}\)。
那么 \(a_i+a_j-i-j>n-\operatorname{dis}(1,n)\)。考察其实际含义:后者是所有点去掉在链上的点数,经过简单画图就可以得知前者一定小于后者,矛盾。
证明:若对任意点 \(i\) 都有 \(a_i-i\le \frac{n-\operatorname{dis}(1,n)}{2}\),一定存在方案使得有完美匹配。
令 \(p_i\) 为实际在 \(i\) 子树内钦定的点的数量,将这样的限制转换为 \(p_i\ge a_i- \frac{n-\operatorname{dis}(1,n)}{2}\)。我们只关心这个式子的右边大于 \(0\) 的情况(小于等于 \(0\) 显然可以先不钦定,构造后再去考虑那些点被钦定)。只有一个 \(i\) 的时候易于构造。
仍然考虑存在 \(i\ne j\) 使得 \(p_j\ge a_j-\frac{n-\operatorname{dis}(1,n)}{2}\),那么:
\(a_i+a_j>n-\operatorname{dis}(1,n)\),仍然考量其实际含义,发现这即限定最多有一个 \(k\ne i\ne j,a_k=2\),其余的 \(a=1\)。
令 \(k=1\),则 \(a_k-k\) 取最大值 \(1\),易于发现 \(\frac{n-\operatorname{dis}(1,n)}{2}\ge1\),此时一定有 \(p_k\le0\) 于是可以仍然不考虑 \(k\)。
不妨认为 \(i<j\),在 \([j+1,n]\) 每个点都填一个点,在 \(j\) 上填 \(a_j-\frac{n-\operatorname{dis}(1,n)}{2}\),\(i\) 上也这么填。剩下的位置也是很容易填的。于是我们证明了对于满足 \(a_i\le\frac{n-\operatorname{dis}(1,n)}{2}\) 的序列一定有解。
考虑唯一的 \(a_x\) 不符合要求的点 \(x\)。就只能向其子树的点走,拉出 \(x\) 的一个子节点 \(v\),将 \(v\) 及其子树视为独立的一部分,与 \(x\) 子树剩下的部分进行匹配。这仍然需要满足 \(siz_v-i\le\frac{n-\operatorname{dis}(1,n)}{2}\),令 \(k=i+\frac{n-\operatorname{dis}(1,n)}{2}\),即 \(siz_v\le k\),发现 \(2k\ge n-\operatorname{dis}(1,n)\),即我们限定合法的 \(v\) 一定在 \(x\) 的重链方向。
有了这个东西直接实现即可。
#include<iostream>
#include<vector>
#include<algorithm>
#define pb push_back
const int N = 2e5 + 10;
int n, rs, lm, d[N] = {-1}, sz[N], f[N], vs[N]; std::vector<int> g[N], vc;
void dfs(int x, int fa){
sz[x] = 1, d[x] = d[f[x] = fa] + 1;
for(auto v:g[x]) if(v != fa) dfs(v, x), sz[x] += sz[v];
}
void fd(int x, int lm){
++rs; for(auto v:g[x]) if(!vs[v]
&& sz[x] > lm) vs[v] = 1, sz[x] -= sz[v], fd(v, lm);
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0),std::cout.tie(0);
std::cin >> n; for(int i = 1, x, y; i < n; i++)
std::cin >> x >> y, g[x].pb(y), g[y].pb(x);
dfs(1, 0); lm = (n - d[n]) / 2;
if((n & 1) != (d[n] & 1)) return std::cout << -1, 0;
for(int i = 1; i <= n; i++) std::sort(g[i].begin(),
g[i].end(), [](int a, int b){return sz[a] > sz[b];});
for(int x = n; x; x = f[x]) vc.push_back(x);
std::reverse(vc.begin(), vc.end());
for(auto x : vc) sz[f[x]] -= sz[x], vs[x] = 1;
for(int x = n; x; x = f[x]) fd(x, (n - d[n] + 2 * d[x]) / 2);
std::cout << rs;
}