zoukankan      html  css  js  c++  java
  • CF611H New Year and Forgotten Tree

    CF611H New Year and Forgotten Tree [* easy]

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

    你需要构造一棵树满足此约束,(nle 2cdot 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 o opt),流量为操作次数,(i o T),流量为 (a_i)

    如果满流,那么就有解,同时根据残量网络容易输出解,这张图的大小为 (6^2+8) 近似于 (50),边数也是点数的级别,非要说复杂度的话大概是 (mathcal O((log_{10} n)^{log_{10} n-2} imes (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
    ", 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
    ", 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
    ", 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 ;
    }
    
  • 相关阅读:
    singleton 单例模式
    try catch finall 结构里的 return
    ConcurrentHashMap
    ConcurrentHashMap原理分析
    Java 实现两个整数变量的交换
    Java reflect 反射 3 Class.forname
    Java reflect 反射 2
    Java reflect 反射 1
    java class load 类加载
    HashMap源码解析
  • 原文地址:https://www.cnblogs.com/Soulist/p/13865961.html
Copyright © 2011-2022 走看看