• 「PKUSC2018」最大前缀和(状压dp)


    状压好题啊。

    一眼看出时间复杂度 (O(n2^n)),然后开始想正解。

    然后设 (dp_i) 为前缀状态为 (i) 时的方案数,所以这时候 (sum_i) 一定是单峰的。

    可以推导出:

    [(1leq j<i) sum_igeq sum_jRightarrow sum_i-sum_jgeq 0 ]

    [(i<jleq n) sum_j<sum_iRightarrow sum_j-sum_i<0 ]

    那么我们相当于求两个序列拼凑起来,一个序列前缀和除第一项始终 (geq 0)(因为 (sum_i-sum_1) 不包括第一项,但是 (sum_n-sum_i) 包括最后一项),一个前缀和始终 (<0)(f_i) 表示前缀和为负数的,(g_i) 表示前缀和为正数的,那么 (g_i) 拼凑时 (i>0)。每一个状态对于答案的贡献为 (g_i imes f_{lim xor i} imes (sum_i+p)\%p)

    时间复杂度 (O(n2^n))

    (Code Below:)

    #include <bits/stdc++.h>
    #define ll long long
    using namespace std;
    const int p=998244353;
    int n,lim,a[20],sum[1<<20],f[1<<20],g[1<<20];
    
    int main()
    {
    	scanf("%d",&n);lim=(1<<n)-1;
    	for(int i=0;i<n;i++) scanf("%d",&a[i]);
    	for(int i=1;i<=lim;i++)
    		for(int j=0;j<n;j++)
    			if(i&(1<<j)){
    				sum[i]=sum[i^(1<<j)]+a[j];
    				break;
    			}
    	f[0]=g[0]=1;
    	for(int i=1;i<=lim;i++)
    		if(sum[i]<0)
    			for(int j=0;j<n;j++)
    				if((i&(1<<j))&&(sum[i^(1<<j)]<0||!(i^(1<<j)))) f[i]=(f[i]+f[i^(1<<j)])%p;
    	for(int i=1;i<=lim;i++)
    		for(int j=0;j<n;j++)
    			if((i&(1<<j))&&(sum[i^(1<<j)]>=0||!(i^(1<<j)))) g[i]=(g[i]+g[i^(1<<j)])%p;
    	int ans=0;
    	for(int i=1;i<=lim;i++) ans=(ans+(ll)g[i]*f[lim^i]%p*(sum[i]+p)%p)%p;
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    USACO 5.1 Starry Night
    USACO 4.4 Frame Up
    USACO 4.4 Shuttle Puzzle
    USACO 4.3 Letter Game (字典树)
    USACO 4.3 Street Race
    BZOJ 1036: [ZJOI2008]树的统计Count (树链剖分模板题)
    BZOJ 1861: [Zjoi2006]Book 书架 (splay)
    codeforces 354 D. Transferring Pyramid
    codeforces 286 E. Ladies' Shop (FFT)
    USACO 4.3 Buy Low, Buy Lower
  • 原文地址:https://www.cnblogs.com/owencodeisking/p/10217824.html
Copyright © 2020-2023  润新知