• P1450 [HAOI2008]硬币购物(完全背包+容斥)


    P1450 [HAOI2008]硬币购物

    暴力做法:每次询问跑一遍多重背包。

    考虑正解

    其实每次跑多重背包都有一部分是被重复算的,浪费了大量时间

    考虑先做一遍完全背包

    算出$f[i]$表示买价值$i$东西的方案数

    蓝后对每次询问价值$t$,减去不合法的方案

    $c_1$超额方案$f[t-c_1*(d_1+1)]$,表示取了$d_1+1$个$c_1$,剩下随便取的方案数(这就是差分数组)

    如法炮制,减去$c_2,c_3,c_4$的超额方案数

    但是我们发现,我们多减了$(c_1,c_2),(c_1,c_3),(c_1,c_4)......$同时超额的方案数

    于是就再把方案数$f[t-c_i*(d_i+1)-c_j*(d_j+1)]$给加回来

    然鹅我们又多加上了$(c_1,c_2,c_3)....$3种硬币同时超额的方案数,于是又要减掉这些方案

    最后再把4种硬币都超额的方案数加回来

    这就是容斥

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    typedef long long ll;
    ll ans,f[100005];
    int c[5],d[5],T,t;
    int main(){
        scanf("%d%d%d%d%d",&c[0],&c[1],&c[2],&c[3],&T);
        f[0]=1;
        for(int i=0;i<=3;i++)
            for(int j=c[i];j<=100000;++j)
                f[j]+=f[j-c[i]];//预处理
        while(T--){
            scanf("%d%d%d%d%d",&d[0],&d[1],&d[2],&d[3],&t);
            ans=f[t];
            for(int i=1;i<16;++i){//二进制枚举子集
                ll now=t,k=1;
                for(int j=0;j<=3;++j)
                    if((i&(1<<j)))
                        k=-k,now-=c[j]*(d[j]+1);
                if(now>=0) ans+=k*f[now];
            }printf("%lld
    ",ans);
        }return 0;
    }
  • 相关阅读:
    JVM StackOverflowError vs. OutOfMemoryError
    db2 command line notes
    my emacs configuration
    repackage android application
    file -i haha.csv
    QualType in clang
    STM in Clojure
    32bit / 64bit co-exist Linux, ld-linux.so, linux-gate.so.1 etc
    hash tree
    K-S Test
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/10778282.html
Copyright © 2020-2023  润新知