CF1430G [* easy]
给定一张 DAG,你需要给每个点分配点权 (a),使得对于一条有向边,(u o v,w),有 ((a_u-a_v)>0)
最小化 (sum w_i imes (a_u-a_v))
(nle 18,mle frac{n(n-1)}{2})
Solution
考虑 (f_x=sum_{x o v}w_i-sum_{v o x}w_i)
答案即为 (sum a_i imes f_i)
限制等价于 DAG 上每个点的点权大于其出边的点权。
考虑按照点权进行分层,第 (i) 层的点权为 (i),那么对答案的贡献即为 (sum i imes sum_{S})
这样太诡异了,考虑差分,变成 (sum sum_{T-S}),(T) 表示全集。
然后就可以类似于宝藏,枚举子集转移了,转移的前提是这个子集本身合法(内部无边)同时转移到的 (ioplus 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 ;
}