bzoj3771. Triple

题解

c n t i cnt_i cnti表示原序列中值为 i i i的元素的个数

构造三个多项式 F ( x ) , G ( x ) , H ( x ) F(x),G(x),H(x) F(x),G(x),H(x)

其中 [ x i ] F ( x ) = c n t i , [ x 2 i ] G ( x ) = c n t i , [ x 3 i ] H ( x ) = c n t i [x^i]F(x)=cnt_i,[x^{2i}]G(x)=cnt_i,[x^{3i}]H(x)=cnt_i [xi]F(x)=cnti,[x2i]G(x)=cnti,[x3i]H(x)=cnti

先算一把斧头的,就是 F ( x ) F(x) F(x)

然后算两把斧头的, 1 2 ( F 2 ( x ) − G ( x ) ) \frac{1}{2}(F^2(x)-G(x)) 21(F2(x)G(x))

然后算三把的, 1 6 { F 3 ( x ) − 3 [ F ( x ) G ( x ) − H ( x ) ] − F ( x ) } \frac{1}{6}\{ F^3(x) - 3 [ F(x)G(x) - H(x) ] - F(x) \} 61{F3(x)3[F(x)G(x)H(x)]F(x)}

代码

#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#define iinf 0x3f3f3f3f
#define linf (1ll<<60)
#define eps 1e-8
#define maxn 300000
#define cl(x) memset(x,0,sizeof(x))
#define rep(i,a,b) for(i=a;i<=b;i++)
#define drep(i,a,b) for(i=a;i>=b;i--)
#define em(x) emplace(x)
#define emb(x) emplace_back(x)
#define emf(x) emplace_front(x)
#define fi first
#define se second
#define de(x) cerr<<#x<<" = "<<x<<endl
using namespace std;
using namespace __gnu_pbds;
typedef long long ll;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
ll read(ll x=0)
{
    ll c, f(1);
    for(c=getchar();!isdigit(c);c=getchar())if(c=='-')f=-f;
    for(;isdigit(c);c=getchar())x=x*10+c-0x30;
    return f*x;
}
struct FFT
{
    int n, R[maxn];
    typedef complex<double> C;
    C a[maxn], b[maxn], w[maxn];
    const double pi=acos(-1);
    void init(int bound)    //bound是积多项式的最高次幂
    {
        int L(0);
        for(n=1;n<=bound;n<<=1,L++);
        for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1)), a[i]=b[i]=C(0,0);
    }
    void fft(C* a, int opt)
    {
        int i, j, k, d;
        for(i=0;i<n;i++)if(i>R[i])swap(a[i],a[R[i]]);
        for(i=0;i<n;i++)w[i]=C(cos(pi*i/n),opt*sin(pi*i/n));
        for(d=0,k=__builtin_ctz(n);(1<<d)<n;d++)
        {
            int m=1<<d, m2=m<<1;
            for(i=0;i<n;i+=m2)for(j=0;j<m;j++)
            {
                C &x=a[i+j+m], &y=a[i+j], t=w[j<<(k-d)]*x;
                x=y-t, y=y+t;
            }
        }
        if(opt==-1)for(i=0;i<n;i++)a[i]/=n;
    }
    void run()
    {
        fft(a,1), fft(b,1);
        for(int i=0;i<n;i++)a[i]*=b[i];
        fft(a,-1);
    }
}fft;
FFT::C F[maxn], G[maxn], H[maxn], A[maxn];
ll ans[maxn], n;
int main()
{
    ll i;
    n=read();
    fft.init(3*40000);
    rep(i,1,n)
    {
        auto one = FFT::C(1,0);
        ll x=read();
        F[x]+=one;
        G[2*x]+=one;
        H[3*x]+=one;
    }
    fft.fft(F,1);
    fft.fft(G,1);
    fft.fft(H,1);
    rep(i,0,fft.n-1)
    {
        A[i] += F[i];
        A[i] += FFT::C(1.0/2) * ( F[i]*F[i] - G[i] );
        A[i] += FFT::C(1.0/6) * ( F[i]*F[i]*F[i] - FFT::C(3)*(F[i]*G[i]-H[i]) - F[i] );
    }
    fft.fft(A,-1);
    rep(i,0,fft.n-1)ans[i]=ll((A[i]+FFT::C(0.5)).real());
    rep(i,0,fft.n-1)if(ans[i])printf("%lld %lld\n",i,ans[i]);
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值