题解 [CF611H] New Year and Forgotten Tree

传送门

给孩子整自闭了.jpg

首先发现可以将位数相同的数缩成一个集合
然后还能发现集合内部有连边的点可以直接缩成一个点
再然后就不会了

  • 对于一部分构造题,尤其是给边定向一类的,考虑图匹配/网络流

以上与本题基本无关
考虑上面已经推得的结论
因为树上不能有环,所以同一集合内的点都是由外面的点连通的
那么经过一些调整可以发现可以让每个集合内只有一个点度数 \(>1\),称这个点为关键点
然后抛弃上面包括缩点在内的一切结论
令集合有 \(m\)
那么本质不同的边有 \(\frac{m(m-1)}{2}\) 种,本质不同的点有 \(m\)

将树定为有向的,令 1 为根
那么整棵树可以看做每条边与其儿子的完美匹配

那么就是一个二分图多重完美匹配
注意到点数小的离谱,而 hall 定理同样适用于多重匹配
于是枚举一种边匹配掉,hall 定理 check 匹配掉后是否还存在完美匹配即可

实现上有一些细节
我试图钦定 \(1,10,100\cdots\) 这些点为关键点,然后各种假
其实可以时刻维护出哪些点已经加入树中,每次加入一个不在树中的
让每个集合中第一个被加入的点做关键点即可省掉一堆细节

最后复杂度是 \(O(nm^22^m)\),跑不满

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long

int n, m;
char s[N], t[N];
vector<int> buc[N];
pair<int, int> sta[N];
bool vis[N], cover[7][7];
map<pair<int, int>, bool> mp;
int e[10][10], siz[N], rep[N], cnt, top;

bool check() {
	// cout<<"check: "<<endl;
	// cout<<"siz: "; for (int i=1; i<=m; ++i) cout<<siz[i]<<' '; cout<<endl;
	// cout<<"---edge---"<<endl; for (int i=1; i<=m; ++i) {for (int j=1; j<=m; ++j) cout<<e[i][j]<<' '; cout<<endl;}
	int lim=1<<m;
	for (int s=1; s<lim; ++s) {
		memset(cover, 0, sizeof(cover));
		int x=0, y=0;
		for (int i=1; i<=m; ++i) if (s&(1<<i-1)) {
			for (int j=1; j<=m; ++j) cover[min(i, j)][max(i, j)]=1;
			x+=siz[i];
		}
		for (int i=1; i<=m; ++i)
			for (int j=i; j<=m; ++j) if (cover[i][j])
				y+=e[i][j];
		if (x>y) return 0;
	}
	return 1;
}

signed main()
{
	scanf("%d", &n);
	// cout<<"m: "<<m<<endl;
	for (int i=1; i<=n; i*=10,++m);
	for (int i=1; i<=n; ++i) {
		int cnt=0;
		for (int t=i; t; t/=10,++cnt);
		siz[cnt]+=(i!=1);
		if (i!=1) buc[cnt].pb(i);
	}
	for (int i=1,u,v; i<n; ++i) {
		scanf("%s%s", s, t);
		u=strlen(s), v=strlen(t);
		++e[min(u, v)][max(u, v)];
	}
	// cout<<check()<<endl;
	vis[1]=1; rep[1]=1;
	for (; cnt<n-1; ++cnt) {
		// cout<<"cnt: "<<cnt<<endl;
		for (int i=1; i<=m; ++i) if (vis[i]) {
			for (int j=1; j<=m; ++j) if (e[min(i, j)][max(i, j)]&&siz[j]) {
				// cout<<"ij: "<<i<<' '<<j<<endl;
				--e[min(i, j)][max(i, j)];
				if (--siz[j], check()) {
					sta[++top]={rep[i], buc[j].back()};
					if (!vis[j]) rep[j]=buc[j].back(), vis[j]=1;
					buc[j].pop_back(); goto jump;
				}
				else ++siz[j];
				++e[min(i, j)][max(i, j)];
			}
		}
		puts("-1"); return 0;
		jump: ;
	}
	assert(top==n-1);
	for (int i=1; i<=top; ++i) printf("%d %d\n", sta[i].fir, sta[i].sec);
	
	return 0;
}
posted @ 2022-05-08 21:35  Administrator-09  阅读(1)  评论(0编辑  收藏  举报