zoukankan      html  css  js  c++  java
  • bzoj1402:[HAOI2008]硬币购物

    思路:完全背包加容斥原理

    首先不考虑限制,那么很容易可以预处理出f[i](f[i]+=f[i-c[i]],1<=i<=4,i-c[i]>=0)。

    然后考虑如何求出限制后的答案。

    首先考虑这样的一个问题:x1+x2+x3+x4+x5+...+xn=m有多少组整数解。显然插板法可以解决这个问题,但如果引入对于xi的限制,令xi不能超过ri,那么这个问题就应该要用到容斥原理了。

    令Si为所有满足条件的xi的集合,那么这个问题就转化为了求所有Si的交集后再用插板法的一个问题了,瓶颈就在于如何求出Si的交集,于是可以考虑容斥原理,Si的交集即全集U-所有Si补集的并集,而Si的补集也就是满足xi>ri即xi>=ri+1的xi的集合,这样令所有的xi-=(ri+1),也就是令m+=(ri+1),然后即可用容斥原理加插板法求出所有Si补集的并集,全集U即原始问题的答案,那么这样运用容斥就完美地解决了这样一个问题。

    再回到我们的问题,可以发现这就是刚刚提到的问题的每一个xi乘上一个权值,那么就令m+=(ri+1)*ci即可,于是对于所有询问均可做到O(1)回答。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 using namespace std;
     7 #define maxn 101000
     8  
     9 int c[5],d[5],num[100],cases;
    10 long long f[maxn];
    11  
    12 int main(){
    13     for (int i=1;i<=4;i++) scanf("%d",&c[i]);scanf("%d",&cases);
    14     f[0]=1;
    15     for (int i=1;i<=4;i++)
    16         for (int j=c[i];j<=100000;j++)
    17             f[j]+=f[j-c[i]];
    18     num[0]=1;
    19     for (int i=1;i<(1<<4);i++) num[i]=num[i>>1]*((i&1)?-1:1);
    20     while (cases--){
    21         int sum;for (int i=1;i<=4;i++) scanf("%d",&d[i]);scanf("%d",&sum);long long ans=f[sum];
    22         for (int i=1;i<(1<<4);i++){
    23             int tmp=0;
    24             for (int j=0;j<5;j++)
    25                 if ((1<<j)&i) tmp+=(d[j+1]+1)*c[j+1];
    26             if (sum>=tmp) ans+=f[sum-tmp]*num[i];
    27         }
    28         printf("%lld
    ",ans);
    29     }
    30     return 0;
    31 }
    View Code
  • 相关阅读:
    Java 7的javax.net.ssl.SSLHandshakeException
    Oracle数据泵导出数据库
    ORA-00054: 资源正忙 --锁表的解决方法
    Linux学习私人笔记-Shell基础
    Linux学习私人笔记-目录和文件的基本命令
    Linux学习私人笔记-文件基本命令
    Linux学习私人笔记-账号管理
    Linux学习私人笔记-Vim
    form提交表单时本地下载
    SQL Servel 中分页解决
  • 原文地址:https://www.cnblogs.com/DUXT/p/5944604.html
Copyright © 2011-2022 走看看