zoukankan      html  css  js  c++  java
  • xsy 1836

    from NOIP2016模拟题36

    Description

    商店里有n种背包和m种物品,物品体积为1到m,背包容积<=m
    给出n个背包的容积
    现在要求出这样一个物品集合,满足:
    1)对于任意一个背包,都能找到这样一个物品的子集,使得这个子集中物品的体积和(每个物品可以使用多次)恰好等于背包的容积

    2)对于每一个体积和小于等于m的物品子集(每个物品可以使用多次),都有一个背包容积恰好等于这个体积和

    求满足条件的最少的物品集合

    Analysis

    有解 (Leftrightarrow) 物品集=背包体积集合 时 有解
    然后我们要缩小集合
    就是判断一个数能不能被小于它的数背包dp出来
    然而由于题目的特殊性质
    只用是否存在两个小于它的数a,b满足a+b等于它
    我们可以用FFT
    简单证明:
    若已经求出有解
    a,b,c,d在物品集中
    则2a,3a.....都在物品集中
    同理a+b,2a+b,4a+2b+3c,2a+4b+5c+d什么的都在集合中
    集合中的数任意两个相加即可表示任何数

    证完了
    回到一开始怎么知道物品集=背包集的情况是否满足条件?
    将物品集conv后看看是否出现新数
    有就不满足

    Code

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <cmath>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    const double pi=acos(-1.0);
    const int M=1000007;
    const int N=2097152;
     
    inline int rd(){
        int x=0;bool f=1;char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=0;
        for(;isdigit(c);c=getchar()) x=x*10+c-48;
        return f?x:-x;
    }
     
    int n,m;
    int rev[N];
    struct CP{
        double x,i;
        CP(double xx=0.0,double ii=0.0){x=xx;i=ii;}
    }a[N];
    CP operator +(CP x,CP y){return CP(x.x+y.x,x.i+y.i);}
    CP operator -(CP x,CP y){return CP(x.x-y.x,x.i-y.i);}
    CP operator *(CP x,CP y){return CP(x.x*y.x-x.i*y.i,x.i*y.x+x.x*y.i);}
     
    void FFT(CP *a,int fl){
        int i,j,k;
        CP W,Wn,u,v;
        for(i=0;i<N;i++) if(rev[i]<i) swap(a[i],a[rev[i]]);
         
        for(i=2;i<=N;i<<=1){
            Wn=CP(cos(2*pi/i),fl*sin(2*pi/i));
            for(j=0;j<N;j+=i){
                W=CP(1,0);
                for(k=j;k<j+i/2;k++,W=W*Wn){
                    u=a[k];
                    v=W*a[k+i/2];
                    a[k]=u+v;
                    a[k+i/2]=u-v;
                }
            }
        }
         
        if(fl==-1)
            for(i=0;i<N;i++) a[i].x=(a[i].x/N)+0.5;
    }
     
    int vis[M];
    int ans=0;
     
    int main(){
        int i,j,k,x;
        n=rd(),m=rd();
        for(i=1;i<=n;i++){
            x=rd();
            vis[x]=1;
            a[x]=CP(1,0);
        }
         
        for(i=0;i<N;i++) rev[i]=(rev[i>>1]>>1)|((i&1)?(N>>1):0);
        FFT(a,1);
        for(i=0;i<N;i++) a[i]=a[i]*a[i];
        FFT(a,-1);
         
        ans=n;
        for(i=1;i<=m;i++)
        if(a[i].x>=1){ //注意现在是double 
            if(!vis[i]){
                puts("NO");
                return 0;
            }
            ans--;
        }
         
        puts("YES");
        printf("%d
    ",ans);
        for(i=1;i<=m;i++)
        if(vis[i]&&a[i].x<1){
            if(ans>1) printf("%d ",i);
            else printf("%d
    ",i);
            ans--;
        }
        return 0;
    }
    
  • 相关阅读:
    centos 7安装gitlab及使用
    jenkins配置自动执行sql脚本
    zabbix监控mysql主从同步
    linux服务器通过mailx邮件发送附件到指定邮箱
    监控pos收银机
    elk安装与配置
    zabbix实时监控mysql业务数据
    Build Eclipse plug-in for Hadoop
    io 一不小心 关闭不住的偶
    hdu 1059二进制优化背包问题
  • 原文地址:https://www.cnblogs.com/acha/p/6366075.html
Copyright © 2011-2022 走看看