zoukankan      html  css  js  c++  java
  • LOJ#6433. 「PKUSC2018」最大前缀和 状压dp

    原文链接https://www.cnblogs.com/zhouzhendong/p/LOJ6433.html

    题解

    枚举一个集合 S ,表示最大前缀和中包含的元素集为 S ,然后求出有多少个排列是这样的。

    对于左边和右边分别考虑,我们可以发现:

    左边:每一个后缀和都 >=0

    右边:每一个前缀和都 <0

    然后就只需要用两个 dp 分别求出每一个集合的元素的排列中分别满足上述条件的方案数即可。

    注意一下题目要求最大前缀和非空。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    typedef unsigned long long ULL;
    int read(){
    	int x=0,f=1;
    	char ch=getchar();
    	while (!isdigit(ch)&&ch!='-')
    		ch=getchar();
    	if (ch=='-')
    		f=0,ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return f?x:-x;
    }
    const int N=20,mod=998244353;
    int n,a[N];
    int Log[1<<N],sum[1<<N],dp1[1<<N],dp2[1<<N];
    void Add(int &x,int y){
    	if ((x+=y)>=mod)
    		x-=mod;
    }
    int main(){
    	n=read();
    	for (int i=0;i<n;i++)
    		a[i]=read();
    	for (int i=2;i<(1<<n);i++)
    		Log[i]=Log[i>>1]+1;
    	for (int i=1;i<(1<<n);i++)
    		sum[i]=sum[i^(i&-i)]+a[Log[i&-i]];
    	dp1[0]=1;
    	for (int i=0;i<n;i++)
    		dp2[1<<i]=1;
    	for (int i=1;i<(1<<n);i++)
    		for (int j=0;j<n;j++)
    			if (i>>j&1){
    				if (sum[i]<0)
    					Add(dp1[i],dp1[i^(1<<j)]);
    				if (sum[i]-a[j]>=0)
    					Add(dp2[i],dp2[i^(1<<j)]);
    			}
    	int ans=0,base=(1<<n)-1;
    	for (int i=1;i<(1<<n);i++)
    		ans=(1LL*sum[i]*dp2[i]%mod*dp1[i^base]+ans)%mod;
    	ans=(ans+mod)%mod;
    	printf("%d",ans);
    	return 0;
    }
    

      

  • 相关阅读:
    TechRoad_oneStep_0509
    TechRoad_0417
    [Network] 判断设备是否能访问 Internet
    云原生相关知识点
    Algorithm: 多项式乘法 Polynomial Multiplication: 快速傅里叶变换 FFT / 快速数论变换 NTT
    Mac下配置Apache服务器
    解决antdv 中input每输入一个字符就失去焦点
    *装饰者模式(Decorator)
    *单例模式(singleton)
    ubuntu20.04 线缆已拔出
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/LOJ6433.html
Copyright © 2011-2022 走看看