• 【bzoj1042】[HAOI2008]硬币购物


    首先使用DP预处理,先求出,在不考虑每种硬币个数的限制的情况下,每个钱数有多少种拼凑方案。

    为了避免重复的方案被转移,所以我们以硬币种类为第一层循环,这样阶段性的增加硬币。

    一定要注意这个第一层循环要是硬币种类,并且初始 f[0] = 1。

    之后对于每个询问 (A1, A2, A3, A4, S) ,根据容斥原理,我们要求的答案 Ans 就是 f[S] - (硬币1超限制的方案数) - (硬币2超限制的方案数) - (硬币3超限制的方案数) - (硬币4超限制的方案数) + (硬币1,2超限制的方案数) + (硬币1,3超限制的方案数) + (硬币1,4超限制的方案数) + .... - (硬币1,2,3超限制的方案数) - ... + (硬币1,2,3,4超限制的方案数) 。

    怎样求硬币1超限制的方案数呢?我们只要先固定取 (A1+1) 个硬币1,剩余的钱数随便取就可以了,就是 f[S - (A1+1) * V[1]] 。

    其余的情况都类似。

    容斥的部分使用搜索实现。

     

     
    #include<algorithm>
    #include<iostream>
    #include<cstdlib>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<queue>
    using namespace std;
     
    #define MAXN 100010
     
    typedef long long LL;
     
    int n;
    int x;
    int a[7],b[7];
     
    LL ans;
     
    LL f[MAXN];
     
    void dfs(int x,int k,int d)
    {
        if (d<0)
            return ;
        if (x==5)
        {
            if (k & 1)
                ans-=f[d];
            else
                ans+=f[d];
            return ;
        }
        dfs(x+1,k+1,d-(a[x]+1)*b[x]);
        dfs(x+1,k,d);
    }
     
    int main()
    {
        for (int i=1;i<=4;i++)
            scanf("%d",&b[i]);
        scanf("%d",&n);
        f[0]=1;
        for (int i=1;i<=4;i++)
            for (int j=b[i];j<=MAXN;j++)
                f[j]+=f[j-b[i]];
        for (int i=1;i<=n;i++)
        {
            for (int j=1;j<=4;j++)
                scanf("%d",&a[j]);
            scanf("%d",&x);
            ans=0;
            dfs(1,0,x);
            printf("%lld
    ",ans);
        }
        return 0;
    }
    

      

     
    还有一个鬼畜算法。。搞不清楚啊。。

    用容斥原理做背包。

    首先,我们要先处理出四种钞票都不限的方案数。

    对于每一个询问,我们利用容斥原理,答案为:得到S所有超过数量限制的方案数-硬币1超过限制的方案数-硬币2超过限制的方案数-硬币3超过限制的方案数-硬币4超过限制的方案数+硬币1、2超过限制的方案数+…+硬币1、2、3、4均超过限制的方案数。

    而对于每种方案数的求法,也非常简单:假设我们要求的是F[S],则硬币1超过限制(即硬币1取的个数≥d[1]+1,不考虑硬币2、3、4是否超过限制)时的方案数即为F[S-(d[1]+1)×c[1]]。

     
    #include <iostream> 
    #include <cstdio> 
    #include <cstring> 
    #include <algorithm> 
    using namespace std; 
    int c[5]; 
    long long F[110000]; 
    struct{long long operator[](int pos){return pos<0?0:F[pos];}}f; 
    int main(int argc, char *argv[]) 
    { 
        int T;scanf("%d%d%d%d%d",&c[1],&c[2],&c[3],&c[4],&T); 
        F[0]=1; 
        for(int i=1;i<=4;i++) 
            for(int j=0;j<=100000;j++) 
            if(j+c[i]<=100000)F[j+c[i]]+=F[j]; 
        while(T--) 
        { 
            int d[5],s;scanf("%d%d%d%d%d",&d[1],&d[2],&d[3],&d[4],&s); 
            long long ans=f[s]; 
            ans-=f[s-(d[1]+1)*c[1]]; 
            ans-=f[s-(d[2]+1)*c[2]]; 
            ans-=f[s-(d[3]+1)*c[3]]; 
            ans-=f[s-(d[4]+1)*c[4]]; 
            ans+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]]; 
            ans+=f[s-(d[1]+1)*c[1]-(d[3]+1)*c[3]]; 
            ans+=f[s-(d[1]+1)*c[1]-(d[4]+1)*c[4]]; 
            ans+=f[s-(d[2]+1)*c[2]-(d[3]+1)*c[3]]; 
            ans+=f[s-(d[2]+1)*c[2]-(d[4]+1)*c[4]]; 
            ans+=f[s-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; 
            ans-=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]]; 
            ans-=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[4]+1)*c[4]]; 
            ans-=f[s-(d[1]+1)*c[1]-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; 
            ans-=f[s-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; 
            ans+=f[s-(d[1]+1)*c[1]-(d[2]+1)*c[2]-(d[3]+1)*c[3]-(d[4]+1)*c[4]]; 
            #ifdef ONLINE_JUDGE 
                printf("%lld
    ",ans); 
            #else 
                printf("%I64d
    ",ans); 
            #endif 
        } 
        return 0; 
    } 
     
    

      

    貌似更快一些= =
  • 相关阅读:
    [暑假集训--数论]hdu2136 Largest prime factor
    [暑假集训--数论]hdu1019 Least Common Multiple
    [暑假集训--数论]poj2115 C Looooops
    [暑假集训--数论]poj1365 Prime Land
    [暑假集训--数论]poj2034 Anti-prime Sequences
    [暑假集训--数论]poj1595 Prime Cuts
    [暑假集训--数论]poj2262 Goldbach's Conjecture
    [暑假集训--数论]poj2909 Goldbach's Conjecture
    [暑假集训--数论]poj3518 Prime Gap
    [暑假集训--数论]poj1730 Perfect Pth Powers
  • 原文地址:https://www.cnblogs.com/yangjiyuan/p/5321103.html
Copyright © 2020-2023  润新知