zoukankan      html  css  js  c++  java
  • 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实现) 洛谷 P1603 斯诺登的密码
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1028 数的计算
    (Java实现) 洛谷 P1028 数的计算
    (Java实现) 洛谷 P1553 数字反转(升级版)
    8.4 确定两个日期之间的月份数或年数
    (Java实现) 洛谷 P1553 数字反转(升级版)
  • 原文地址:https://www.cnblogs.com/Scx117/p/8707219.html
Copyright © 2011-2022 走看看