• bzoj3771 Triple


    题意:一共有n个物品,价值都不同。问取一件或两件或三件物品,可能得到的总价值有哪些,并对于每个价值输出有多少种和为该价值的取物方案(无序)。

    n,ai<=40000;

    标程:

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<complex> 
     5 #include<cmath> 
     6 using namespace std;
     7 typedef complex<double> p;
     8 const double pi=acos(-1);
     9 const int N=160000;
    10 int n,l,pos[N],f[N],g[N],tt[N],a[N],Max,inv_n,ans[N];
    11 p x[N],y[N],xx[N],tmp[N];
    12 void init()
    13 {
    14     l=0;
    15     for (;(1<<l+1)<=n;l++); 
    16     n=1<<++l; 
    17     for (int i=1;i<=n;i++)//循环从1开始 
    18       pos[i]=(i&1)?pos[i-1]|(1<<l-1):pos[i>>1]>>1;
    19 }
    20 void fft(p *a)
    21 {
    22     for (int i=0;i<=n;i++) tmp[i]=a[pos[i]];//循环从0开始 
    23     int len=1;
    24     for (int i=0;i<l;i++)
    25     {
    26         p wn=p(cos(-pi/len),sin(-pi/len));
    27         for (int j=0;j<n;j+=len*2)
    28         {
    29           p w=p(1,0);
    30           for (int k=j;k<j+len;k++)
    31           {
    32                 p l=tmp[k],r=tmp[k+len]*w;
    33                 tmp[k]=l+r;tmp[k+len]=l-r;
    34                 w*=wn;
    35           }
    36        }
    37         len<<=1;
    38     }
    39     for (int i=0;i<=n;i++) a[i]=tmp[i];//复数不要用memset 
    40 }
    41 void fft(p *a,int op)
    42 {
    43     fft(a);
    44     if (op==-1) 
    45     {
    46         reverse(a+1,a+n);//注意第0位和第n位不交换 
    47         for (int i=0;i<=n;i++) a[i]/=n;
    48     }
    49 }
    50 int main()
    51 {
    52     scanf("%d",&n);int nn=n;
    53     for (int i=1;i<=n;i++) scanf("%d",&a[i]),xx[a[i]]=(1),y[2*a[i]]=(1),Max=max(Max,a[i]);
    54     n=Max*3;init();
    55     //选三个东西 
    56     fft(xx,1);fft(y,1);
    57     for (int i=0;i<=n;i++) y[i]=y[i]*xx[i];
    58     for (int i=0;i<=n;i++) x[i]=xx[i]*xx[i]*xx[i];
    59    fft(x,-1);fft(y,-1);
    60    for (int i=0;i<=n;i++) g[i]=(int)(x[i].real()+0.5),f[i]=(int)(y[i].real()+0.5);
    61     for (int i=0;i<=n;i++) g[i]-=f[i]*3;
    62     for (int i=1;i<=nn;i++) g[3*a[i]]+=2;
    63     for (int i=0;i<=n;i++) ans[i]=g[i]/6;
    64     //选两个东西 
    65    for (int i=0;i<=n;i++) xx[i]=xx[i]*xx[i];
    66    fft(xx,-1); for (int i=0;i<=n;i++) g[i]=(int)(xx[i].real()+0.5);
    67     for (int i=1;i<=nn;i++) g[2*a[i]]--;
    68    for (int i=0;i<=n;i++) ans[i]+=g[i]/2;
    69     //选一个东西
    70     for (int i=1;i<=nn;i++) ans[a[i]]++;      
    71     for (int i=0;i<n;i++) if (ans[i]) printf("%d %d
    ",i,ans[i]);//注意最后一个n不纳入计算 
    72    return 0;
    73 }

    易错点:1.fft太久不写,有点生疏了。

    2.注意循环的开始从1还是0.

    3.reverse(a+1,a+n).

    4.最后n位置不计算。

    5.不要以为没有模数就可以乱设模数,要用复数。

    题解:fft+生成函数

    比如选一个价值为2的物品,设为x^2.

    最后多项式f(x)中x^k的系数表示总价值为k的方案数。

    所以多项式卷积两次就是选两个,卷三次就是选三个物品。

    还要减去重复计算的,以及去掉本质相同的东西。容斥。

    以取三个物品为例:f(x)^3任意选三个,减去f2(x)*f(x)选两个一样第三个任选*3(三种排列方式),加上同一个数选三次的方案*2(被多减了两次)。

    剩下的就是有序的不重复取同一个东西的取法数。除以6去重。

  • 相关阅读:
    java 多线程面试题
    finally语句块一定会被执行吗
    redis 数据结构
    哪些可以作为GC ROOT
    mybatis 源码分析--日志分析
    mybatis selectKey
    spring cache 和redis
    kafka是如何保证消息不被重复消费的
    kafka面试题及答案
    浅谈:2019 前端面试题
  • 原文地址:https://www.cnblogs.com/Scx117/p/8707219.html
Copyright © 2020-2023  润新知