CF1396E Distance Matching
Solution
我们先考虑 \(k\) 的合法范围。可以发现:
\[\sum_{u} [\text{siz}_u\mod 2]\le k\le \sum_{u} \min(\text{siz}_u,n-\text{siz}_u)
\]
就是对于每一条边考虑它至少/至多被覆盖多少次。
另外一个事实是最大值是可构造的。我们可以找到该树的重心,将其设为根,那么每个子树大小 \(\le n/2\)。具体来说,我们可以取出每个点的 \(\text{dfs}\) 序,\(\text{dfn}_i\) 与 \(\text{dfn}_{i+n/2}\) 匹配即可,这样一定满足每个点都往子树外匹配。
考虑到我们的贡献形如 \(\sum_{(x,y)} \text{dep}_u+\text{dep}_v-2\text{dep}_{\text{lca}}\),也就是说对于一个点只能改变它的 lca,那么 \(k\) 应当与最大值同奇偶。
然后我们根据套路就可以合理猜想该条件是充分的,证明过程就是构造过程。
我们考虑一个不是叶子节点的 \(u\),如果我们想要使答案减少 \(2\text{dep}_u\),我们可以考虑让它和它的儿子中的两个匹配,然后变成子问题继续处理即可。
可以用 set 之类的数据结构维护,时间复杂度 \(\Theta(n\log n)\)。
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define int long long
#define MAXN 100005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,K;
vector <int> g[MAXN];
int rt,cnt,par[MAXN],dfn[MAXN],dep[MAXN],out[MAXN],siz[MAXN],mxs[MAXN],bel[MAXN];
void dfs (int u,int fa){
siz[u] = 1;
for (Int v : g[u]) if (v ^ fa) dfs (v,u),chkmax (mxs[u],siz[v]),siz[u] += siz[v];
chkmax (mxs[u],n - siz[u]),rt = !rt ? u : (mxs[rt] < mxs[u] ? rt : u);
}
#define pii pair<int,int>
#define se second
#define fi first
set <pii> S[MAXN],st;
void dfs1 (int u,int fa,int beg){
par[u] = fa,dep[u] = dep[fa] + 1,siz[u] = 1,bel[u] = beg;
for (Int v : g[u]) if (v ^ fa) dfs1 (v,u,beg),out[u] ++,siz[u] += siz[v];
if (out[u]) S[beg].insert ({dep[u],u});
}
bool vis[MAXN];
void delit (int u){
int x = par[u];
out[x] --,vis[u] = 1;
if (!out[x]) S[bel[x]].erase ({dep[x],x});
}
int pro[MAXN];
void getmatch (int x){
int tot = 0;
for (Int v : g[x]) if (v != par[x] && !vis[v]) pro[++ tot] = v;
if (!vis[x]) pro[++ tot] = x;
printf ("%d %d\n",pro[1],pro[2]),delit (pro[1]),delit (pro[2]);
}
void dfs2 (int u,int fa){
if (!vis[u]) dfn[++ cnt] = u;
for (Int v : g[u]) if (v ^ fa) dfs2 (v,u);
}
signed main(){
read (n,K);
for (Int i = 2,u,v;i <= n;++ i) read (u,v),g[u].push_back (v),g[v].push_back (u);
dfs (1,0);
int miv = 0,mxv = 0;
for (Int u = 1;u <= n;++ u) miv += (siz[u] & 1),mxv += min (siz[u],n - siz[u]);
if (K < miv || K > mxv || ((K & 1) != (mxv & 1))) return puts ("NO") & 1;
puts ("YES");
for (Int u : g[rt]){
dfs1 (u,rt,u);
if (siz[u] > 1) st.insert ({siz[u],u});
}
int det = mxv - K;
while (det){
int x = st.rbegin() -> second,y = S[x].rbegin() -> second;st.erase ({siz[x],x});
if (2 * dep[y] >= det){
y = S[x].lower_bound ({det / 2,0}) -> second,getmatch (y);
break;
}
getmatch (y),det -= 2 * dep[y],siz[x] -= 2;
if (siz[x] > 1) st.insert ({siz[x],x});
}
dfs2 (rt,0);
for (Int i = 1;i <= cnt / 2;++ i) printf ("%d %d\n",dfn[i],dfn[i + cnt / 2]);
return 0;
}