CF1430G

CF1430G [* easy]

给定一张 DAG,你需要给每个点分配点权 \(a\),使得对于一条有向边,\(u\to v,w\),有 \((a_u-a_v)>0\)

最小化 \(\sum w_i\times (a_u-a_v)\)

\(n\le 18,m\le \frac{n(n-1)}{2}\)

Solution

考虑 \(f_x=\sum_{x\to v}w_i-\sum_{v\to x}w_i\)

答案即为 \(\sum a_i\times f_i\)

限制等价于 DAG 上每个点的点权大于其出边的点权。

考虑按照点权进行分层,第 \(i\) 层的点权为 \(i\),那么对答案的贡献即为 \(\sum i\times sum_{S}\)

这样太诡异了,考虑差分,变成 \(\sum sum_{T-S}\)\(T\) 表示全集。

然后就可以类似于宝藏,枚举子集转移了,转移的前提是这个子集本身合法(内部无边)同时转移到的 \(i\oplus S\) 满足不存在到 \(S\) 的边,也预处理一下即可。

复杂度 \(\mathcal O(3^n)\),CF 少爷机,貌似可以过。

\(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 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 N = 20 ; 
const int inf = 1e16 + 7 ; 
int n, m, cnt, f[N], a[N][N], b[N], w[N], g[1 << 18], bit[1 << 18] ;
long long dp[1 << 18], fr[1 << 18], sum[1 << 18] ; 
bool vis[1 << 18] ; 
vector<int> G[N] ;
void print(int S) {
	if( S == 0 ) return ; 
	print(fr[S]) ;
	++ cnt ; int u = (S ^ fr[S]) ;
	rep( j, 1, n ) {
		if((1 << (j - 1)) & u) b[j] = cnt ; 
	}
}
signed main()
{
	n = gi(), m = gi() ; int x, y, z ; 
	rep( i, 1, m ) {
		x = gi(), y = gi(), z = gi() ; 
		a[x][y] = 1, f[x] += z, f[y] -= z ; 
		G[x].pb(y) ; 
	}
	int lim = (1 << n) - 1 ; 
	rep( i, 0, lim ) {
		rep( j, 1, n ) if( (1 << (j - 1)) & i ) sum[i] += f[j], ++ bit[i] ;  
		rep( j, 1, n ) if( (1 << (j - 1)) & i ) w[j] = 1 ; 
		rep( j, 1, n ) if( (1 << (j - 1)) & i ) {
			for(int v : G[j]) if( w[v] ) vis[i] = 1 ;
			for(int v : G[j]) g[i] |= (1 << (v - 1)) ; 
		}
		rep( j, 1, n ) w[j] = 0 ; 
	}
	memset( dp, 63, sizeof(dp) ) ;
	dp[0] = 0 ;
	rep( S, 1, lim ) {
		for(re int i = S; ; i = (i - 1) & S ) {
			if( vis[i ^ S] || (g[i] & (i ^ S)) ) {
				if( i == 0 ) break ; 
				continue ; 
			}
			if( i == S ) continue ; 
			if( dp[S] > dp[i] + sum[lim ^ i] ) {
				dp[S] = dp[i] + sum[lim ^ i] ;
				fr[S] = i ; 
			}
			if( i == 0 ) break ; 
			continue ; 
		}
	}
	print(lim) ; 
	rep( j, 1, n ) printf("%d ", b[j] ) ;  
	return 0 ;
}
posted @ 2020-10-12 18:49  Soulist  阅读(330)  评论(2编辑  收藏  举报