CF611H New Year and Forgotten Tree

CF611H New Year and Forgotten Tree [* easy]

给定 \(n\) 个节点的树,我们现在不知道这棵树的结构,但是我们给出了每条边连接的两个端点的十进制下的位数。

你需要构造一棵树满足此约束,\(n\le 2\cdot 10^5\)

Solution

不是吧这有 3k2 ?

首先不难观察发现所有十进制下位数相同的元素是等价的,所以对于 ?? ?? 的连边我们可以直接连在 10 xx 上。

然后基于调整法,我们一定可以先将 1,10,100,1000... 联通,然后再将 \(x\) 一个一个的挂上去。

因为不难发现我们连接形如 \((2,50),(50,1)\) 再联通此图可以调整为连接 \((2,10)\)\((50,1)\) 使得这张图联通。

然而 \(1,10...10^5\) 之间的具体连边关系会影响答案,但是本质不同的树的数量只有 \(6^4\) 个,所以暴力枚举!

之后 check 答案,不难发现此时问题变成我们有 \(k\) 种操作,每次操作为给两个元素中的一个 \(+1\),最后需要使得所有元素的大小恰好为 \(a_i\)(每个元素代表十进制下位数相同的集合),那么此时就有解。

显然可以网络流建模,考虑对每种操作建一个点,向他所连接的两个集合分别连一条边,边权均为 \(\infty\),然后 \(S\to opt\),流量为操作次数,\(i\to T\),流量为 \(a_i\)

如果满流,那么就有解,同时根据残量网络容易输出解,这张图的大小为 \(6^2+8\) 近似于 \(50\),边数也是点数的级别,非要说复杂度的话大概是 \(\mathcal O((\log_{10} n)^{\log_{10} n-2}\times (\log_{10} n)^6)\) 大概是 \(\mathcal O((\log_{10} n)^{\log_{10} n+4})?\)

反正都能跑就对了。。

  • 枚举树是写的枚举 prufer 序列,然后这玩意儿是擓的以前打 EA 的比赛时写的暴力枚举树的代码,最好还是复习一下:

  • prufer 序列的生成,找到编号最小且度数为 \(1\) 的点,将他的父亲加入 prufer 序列结尾,删除它。

  • prufer 序列的还原,维护所有元素的出现次数,找到最小的没在序列中的元素,让它向第一个元素连边,然后删除这个元素(或者标记为 \(-1\))。最后将两个元素连边。

代码量虽然是 4k,但是我几乎没有调就过了,还是挺舒服的。

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define vi vector<int>
#define pb push_back
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
	return cn * flus ;
}
const int inf = 1e9 + 7 ; 
const int N = 2e5 + 5 ; 
const int M = 100 + 5 ; 
int n, m, a[10], c[10][10], bg[10], fr[10] ; 
int Id(int x, int y) { return (x - 1) * m + y ; }
struct Max_Flow {
	struct E {
		int to, next, w ; 
	} e[M << 1] ;
	int cnt, S, T, dep[M], head[M], cur[M] ; 
	void add(int x, int y, int z) {
		e[++ cnt] = (E){ y, head[x], z }, head[x] = cnt,
		e[++ cnt] = (E){ x, head[y], 0 }, head[y] = cnt ; 
	}
	queue<int> q ; 
	bool bfs() {
		memset( dep, 0, sizeof(dep) ), q.push(S), dep[S] = 1 ; 
		while( !q.empty() ) {
			int u = q.front() ; q.pop() ; 
			Next( i, u ) {
				int v = e[i].to ; 
				if( !dep[v] && e[i].w ) 
				dep[v] = dep[u] + 1, q.push(v) ; 
			}
		} return (dep[T] != 0) ; 
	}
	int dfs(int x, int dist) {
		if( x == T ) return dist ; int flow = 0 ; 
		for(re int &i = cur[x]; i; i = e[i].next ) {
			int v = e[i].to ;
			if( (dep[v] == dep[x] + 1) && e[i].w ) {
				int di = dfs(v, min(dist, e[i].w)) ;
				e[i].w -= di, e[i ^ 1].w += di,
				flow += di, dist -= di ;
				if( !dist ) return flow ; 
			}
		} return flow ; 
	}
	int dinic() {
		int ans = 0 ;
		while(bfs()) {
			memcpy(cur, head, sizeof(head)) ;
			while(int di = dfs(S, inf)) ans += di ;
		} return ans ; 
	}
	void init() { memset( head, 0, sizeof(head) ), cnt = 1 ; }
	void out() {
		rep( x, 1, m ) {
			int u = m * m + x ; 
			Next( i, u ) {
				int v = e[i].to ; 
				if( !e[i].w || (v == T) ) continue ; 
				rep( j, 1, x ) if(((j - 1) * m + x) == v) 
				while( e[i].w ) printf("%d %d\n", fr[j], bg[x] ), ++ bg[x], -- e[i].w ; 
				rep( j, x + 1, m ) if(((x - 1) * m + j) == v)
				while( e[i].w ) printf("%d %d\n", fr[j], bg[x] ), ++ bg[x], -- e[i].w ; 
			}
		}
	}
} flow ; 
char s[M] ; 
int Num, b[M], vis[M], f[10][10] ; 
void dec(int x, int y) { -- f[x][y], -- f[y][x] ; }
void check() {
	rep( i, 1, m ) vis[i] = 0 ; 
	rep( i, 1, m ) rep( j, 1, m ) f[i][j] = c[i][j] ; 
	for( re int i = 1; i < m - 1; ++ i ) ++ vis[b[i]] ;
	for( re int j = 1; j < m - 1; ++ j ) 
		rep( k, 1, m ) if( vis[k] == 0 ) {
			dec( k, b[j] ), -- vis[k], -- vis[b[j]] ; 
			break ; 
		}
	rep( k, 1, m ) if( !vis[k] ) {
		rep( j, k + 1, m ) if( vis[j] == 0 ) { 
			dec( j, k ) ; break ; 
		} break ; 
	}
	rep( i, 1, m ) rep( j, i, m ) if( f[i][j] < 0 ) return ; 
	flow.init() ; int cost = 0 ; 
	flow.S = 0, flow.T = m * (m + 1) + 1 ; 
	rep( i, 1, m ) rep( j, i, m ) {
		flow.add(flow.S, Id(i, j), f[i][j]), cost += f[i][j] ; 
		flow.add(Id(i, j), i + m * m, inf) ;
		if( i != j ) flow.add(Id(i, j), j + m * m, inf) ; 
	}
	rep( i, 1, m ) flow.add(m * m + i, flow.T, a[i]) ; 
	int ans = flow.dinic() ; if( ans != cost ) return ; 
	flow.out() ; rep( i, 1, m ) rep( j, i, m ) 
		if( f[i][j] < c[i][j] ) printf("%d %d\n", fr[i], fr[j] ) ; 
	exit(0) ; 
}
void Dfs(int x) {
	if( (x == m - 1) || (m == 1) ) return check(), void() ; 
	rep( i, 1, m ) b[x] = i, Dfs(x + 1) ;
} 
signed main()
{
	n = gi() ; int x, y ; 
	rep( i, 2, n ) {
		scanf("%s", s + 1 ), x = strlen(s + 1) ;
		scanf("%s", s + 1 ), y = strlen(s + 1) ; 
		if( x > y ) swap( x, y ) ; ++ c[x][y] ;
	}
	int len = n, z = 1 ; 
	while( len ) len /= 10, ++ m ; 
	rep( i, 1, m ) a[i] = z * 9, bg[i] = z + 1, fr[i] = z, z = z * 10 ; 
	a[m] = (n - z / 10) + 1 ; rep( i, 1, m ) -- a[i] ; 
	Dfs(1), puts("-1") ; 
	return 0 ;
}
posted @ 2020-10-23 20:09  Soulist  阅读(115)  评论(0编辑  收藏  举报