• 51Nod 1597 有限背包计数问题


    首先这是一个多重背包

    但是它的数据非常特殊,我们可以利用其性质优化算法

    一个显然的优化是
    (i>sqrt n)时,可以取消个数限制
    (f[i][j])表示选了(i)个物品,体积为(j)的方案数
    一共有两种转移
    可以由(i-1)个物品加上一个最小的物品(sqrt n+1)
    可以由(i)个物品全部加一(全部换成下一个)
    (f[i][j]=f[i-1][j-(sqrt n+1)]+f[i][j-i])
    考虑得到一个答案的过程
    对于每一个过程都会得到一个合法的答案
    对于每一个合法的答案都有唯一一个过程

    因为对于每一个方案
    如果其中存在最小的物品
    那么一定是由第一种方案转移
    否则一定是由第二种方案转移

    比如(n=16)

    体积为(16)的一种方案是(5+5+6)
    这个状态一定由(5+6)转移来
    因为(4=sqrt 16),比最小的物品还小
    所以(4+4+5)是不可能的

    体积为(12)的一种方案是(6+6)
    这个状态一定由(5+5)转移来
    因为方案里并没有(5)(最小的物品)

    所以方案与过程一一对应
    复杂度(O(nsqrt n))

    剩下的是多重背包
    (f[i][j]=sum_{k=0}^{i}{f[i-1][j-i*k]})
    按模(i)分类
    复杂度(O(nsqrt n))

    最后枚举多少空间给前(sqrt n)个物品(剩下空间给其它物品)
    统计答案即可
    复杂度(O(nsqrt n))

    #include<bits/stdc++.h>
    
    using namespace std;
    
    #define gc c=getchar()
    #define r(x) read(x)
    
    template<typename T>
    inline void read(T&x){
        x=0;T k=1;char gc;
        while(!isdigit(c)){if(c=='-')k=-1;gc;}
        while(isdigit(c)){x=x*10+c-'0';gc;}x*=k;
    }
    
    const int p=23333333;
    const int N=1e5+7;
    
    int sum[N],f1[2][N],f2[2][N];
    
    inline int add(int a,int b){
    	a+=b;
    	if(a>=p)a-=p;
    	return a;
    }
    
    int main(){
    	int n;r(n);
    	int t=sqrt(n);
    	
    	f1[0][0]=1;
    	for(int i=1;i<=t;++i){
    		for(int j=0;j<=n;++j){
    			sum[j%i]=add(sum[j%i],f1[i&1^1][j]);
    			if(j-i*(i+1)>=j%i)sum[j%i]=add(sum[j%i],p-f1[i&1^1][j-i*(i+1)]);
    			f1[i&1][j]=sum[j%i];
    		}
    		memset(sum,0,i<<2);
    	}
    	
    	memset(sum,0,(n+1)<<2);
    	f2[0][0]=1;
    	for(int i=1;i<=t;++i){
    		memset(f2[i&1]+(i-1)*(t+1),0,(t+1)<<2);
    		for(int j=i*(t+1);j<=n;++j){
    			f2[i&1][j]=add(f2[i&1][j-i],f2[i&1^1][j-(t+1)]);
    			sum[j]=add(sum[j],f2[i&1][j]);
    		}
    	}
    	
    	sum[0]=1;
    	long long ans=0;
    	for(int i=0;i<=n;++i)(ans+=1ll*f1[t&1][i]*sum[n-i])%=p;
    	
    	printf("%lld
    ",ans);
    }
    
    
  • 相关阅读:
    c++的deque和queue和stack
    c++ vector和set的区别
    c++ set的用法
    c++map的用法
    c++总的map和set有什么区别,如何实现的
    1208. Get Equal Substrings Within Budget
    1089. Duplicate Zeros
    1202. Smallest String With Swaps
    1122. Relative Sort Array
    1144. Decrease Elements To Make Array Zigzag
  • 原文地址:https://www.cnblogs.com/yicongli/p/9788158.html
Copyright © 2020-2023  润新知