• 洛谷P4241 采摘毒瘤


    传送门

    完了我连背包都不会了……

    考虑暴力,先枚举最小的数是哪个,设大小为$d_i$,个数为$k_i$,所有比它小的数的总和是$sum$,然后把所有比它小的全都装进背包,它以及比他大的做一个多重背包,那么设$dp[j]$表示在剩下的这些数里取的总和为$j$时的方案数,那么$$ans+=sum_{j=m-sum-d_i+1}^{m-sum} dp[j]$$

    上面的式子意思就是,将所有比它小的全放进去之后,枚举背包剩余的空间,统计所有方案数。因为不能让把任何大于等于它的在统计之前就放进去,所以下界是$m-sum-d_i+1$

    然后现在的问题就是怎么统计了才能过。我们可以从大到小枚举,这样的话每做到一个物品就只需要对它自己做多重背包,不需要重新dp了

    接下来的操作真的是学到了……在做多重背包的时候把$j$按照模$d_i$的取值分为不同的组,然后时间复杂度就能优化到$O(nm)$

    简单来讲的话,就是转移的时候$dp[j]+=dp[j-d_i]+dp[j-d_i*2]...+dp[j-d_i*k_i]$,发现每一个$j$只会被与它模$d_i$同余的转移到,于是我们枚举这一个余数,统计与它同余的数的前缀和,然后从小到大转移并不断维护前缀和即可(真的很妙)

     1 //minamoto
     2 #include<iostream>
     3 #include<cstdio>
     4 #include<algorithm>
     5 using namespace std;
     6 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
     7 char buf[1<<21],*p1=buf,*p2=buf;
     8 int read(){
     9     #define num ch-'0'
    10     char ch;bool flag=0;int res;
    11     while(!isdigit(ch=getc()))
    12     (ch=='-')&&(flag=true);
    13     for(res=num;isdigit(ch=getc());res=res*10+num);
    14     (flag)&&(res=-res);
    15     #undef num
    16     return res;
    17 }
    18 const int N=505,M=1e5+5,mod=19260817;
    19 struct node{
    20     int k,d;
    21     inline bool operator <(const node &b)const{return d<b.d;}
    22 }a[N];
    23 int n,m,ans,tot,dp[2][M],t;
    24 inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    25 inline int del(int x,int y){return x-y<0?x-y+mod:x-y;}
    26 void ins(int k,int w){
    27     int sum,H;
    28     for(int d=0;d<=w-1;++d){
    29         H=sum=0;
    30         for(int j=0;j<=(m-d)/w;++j){
    31             sum=add(sum,dp[t^1][j*w+d]);
    32             if(H<j-k) sum=del(sum,dp[t^1][(H++)*w+d]);
    33             dp[t][j*w+d]=sum;
    34         }
    35     }
    36 }
    37 int main(){
    38 //    freopen("testdata.in","r",stdin);
    39     n=read(),m=read();
    40     for(int i=1;i<=n;++i) a[i].k=read(),a[i].d=read(),tot+=a[i].k*a[i].d;
    41     if(tot<=m) return puts("1"),0;
    42     sort(a+1,a+1+n);
    43     dp[0][0]=1;
    44     for(int i=n;i;--i){
    45         tot-=a[i].k*a[i].d,t^=1;
    46         ins(a[i].k-1,a[i].d);
    47         for(int j=max(m-tot-a[i].d+1,0);j<=m-tot;++j) ans=add(ans,dp[t][j]);
    48         ins(a[i].k,a[i].d);
    49     }
    50     printf("%d
    ",ans);
    51     return 0;
    52 }
  • 相关阅读:
    java判断字符串是否为数字
    门萨高智商者的集中营
    Android全局变量是用public&amp;nbsp…
    oracle 关闭查询的进程
    oracle 常用参考
    oracle创建临时表
    透明网关设置
    透明网关diy
    又一个下拉菜单导航按钮
    数据库备份或导出时丢失主键的相关知识
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/9827076.html
Copyright © 2020-2023  润新知