BZOJ3771 - Triple

Portal

Description

给出\(n\)个互不相同的数\(\{a_n\}(a_i\leq4\times10^4)\),从中选出一至三个数,其和为\(sum\)。求对于所有可能的\(sum\)各有多少种方案。

Solution

生成函数+容斥原理+FFT。
定义\(A(x)\)为仅选取一个数的生成函数,即\(A(a_i)=1\)\(B(x)\)为仅选取两个相同数的生成函数,即\(B(2a_i)=1\)\(C(x)\)为仅选取三个相同数的生成函数,即\(C(3a_i)=1\)

仅选取一个数的生成函数为\(A\)
仅选取两个数的生成函数为\((A*A-B)/2\)
仅选取三个数的生成函数为\((A*A*A-3A*B+2C)/6\)

依容斥原理简单解释一下。选取两个数:等价于随便选两个数\(x_1,x_2\)\(A*A\)),减去\(x_1=x_2\)\(B\)),因为选择有顺序所以除以\(2!\)。选取三个数:等价于随便选三个数\(x_1,x_2,x_3\)\(A*A*A\)),减去\(x_1=x_2\)\(x_2=x_3\)\(x_3=x_1\)\(3A*B\)),加上两倍(因为被减三次)\(x_1=x_2=x_3\),再除以\(3!\)
求卷积的话用FFT就好。

\(n_0=3max\{a\}\),时间复杂度为\(O(n_0logn_0)\)

Code

//Triple
#include <complex>
#include <cstdio>
typedef long long lint;
typedef std::complex<double> cpx;
const int N=15e4;
const double PI=acos(-1);
int n;
int n0,t,pos[N];
cpx a[N],b[N],c[N],ans[N];
int max(int x,int y) {return x>y?x:y;}
void FFT(cpx x[],int f)
{
    for(int i=0;i<n0;i++) if(i<pos[i]) swap(x[i],x[pos[i]]);
    for(int i=1;i<n0;i<<=1)
    {
        cpx Wn=cpx(cos(PI/i),f*sin(PI/i));
        for(int j=0;j<n0;j+=i<<1)
        {
            cpx w=1;
            for(int k=0;k<i;k++,w*=Wn)
            {
                cpx p=x[j+k],q=w*x[j+k+i];
                x[j+k]=p+q,x[j+k+i]=p-q;
            }
        }
    }
    if(f==-1) for(int i=0;i<n0;i++) x[i]/=n0;
}
int main()
{
    scanf("%d",&n); int m=0;
    for(int i=1;i<=n;i++)
    {
        int x; scanf("%d",&x);
        m=max(m,x);
        a[x]=b[x+x]=c[x+x+x]=1;
    }
    n0=1,t=0; while(n0<m+m+m) n0<<=1,t++;
    for(int i=0;i<n0;i++) pos[i]=pos[i>>1]>>1|(i&1)<<t-1;
    FFT(a,1),FFT(b,1),FFT(c,1);
    for(int i=0;i<n0;i++)
    {
        ans[i]=a[i];
        ans[i]+=(a[i]*a[i]-b[i])/2.0;
        ans[i]+=(a[i]*a[i]*a[i]-3.0*a[i]*b[i]+2.0*c[i])/6.0;
    }
    FFT(ans,-1);
    for(int i=0;i<n0;i++)
    {
        lint x=(lint)(ans[i].real()+0.5);
        if(x) printf("%d %lld\n",i,x);
    }
    return 0;
}

转载于:https://www.cnblogs.com/VisJiao/p/BZOJ3771.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值