Live2D

CF1396E Distance Matching

link

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;
}
posted @ 2022-10-21 18:50  Dark_Romance  阅读(19)  评论(0编辑  收藏  举报