LOJ #6433. 「PKUSC2018」最大前缀和(状压dp)

题面:点击打开链接

题解:点击打开链接(来自一位大佬)

学到了几点:算sum[S]可以用lowbitO(2^n)求解

   求最大前缀和其实考虑什么情况满足是最大前缀和即可

一段和为0时要用最小表示法去重,相同的前缀和只统计一次。


#include<bits/stdc++.h>
using namespace std;
#define maxn 1200000
#define lowbit(x) (x&(-x))

typedef long long ll;
const ll p = 998244353;
int n,a[50],sum[maxn],N,num[maxn];
ll f[maxn],g[maxn],ans;

void init(){
	N = 1 << n;
/*	for (int i = 1 ; i <= 20 ; i++){
		cout<<i<<" "<<lowbit(i)<<endl;
	}*/
	for (int i = 0 ; i < N ; i++){
		sum[i] = sum[i ^ lowbit(i)] + num[lowbit(i)];
	}
}
inline void Add(ll &x,ll y){
	x += y;
	if ( x >= p ) x -= p;
}
int main(){
	freopen("input.txt","r",stdin);
	scanf("%d",&n);
	for (int i = 0 ; i < n ; i++) scanf("%d",&a[i]) , num[1 << i] = a[i];
	init();
	f[0] = g[0] = 1;
	for (register int i = 0 ; i < N ; i++) if ( !i || (f[i] && sum[i] > 0) ){
		for (register int j = 0 ; j < n ; j++){
			if ( !((i >> j) & 1) ){
				Add(f[i | (1 << j)],f[i]);
			}
		}
	}
	for (register int i = 0 ; i < N ; i++) if ( g[i] ){
		for (register int j = 0 ; j < n ; j++){
			if ( !((i >> j) & 1) && (sum[i] + a[j] <= 0) ){
				Add(g[i | (1 << j)],g[i]);
			}
		}
	}
	for (register int i = 1 ; i < N ; i++){
		ans = (ans + f[i] * g[(N - 1) ^ i] % p * sum[i]) % p; 
	}
	cout<<(ans % p + p) % p<<endl;
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42484877/article/details/80904954
今日推荐