bzoj3771: Triple

Posted AKCqhzdy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了bzoj3771: Triple相关的知识,希望对你有一定的参考价值。

码完这题有一种很爽很放松的感觉。。。155行。。。2.5个中午+1个早上真的值,毕竟我没mod题解啊!!!

这题一开始想的时候就想到了补0价值的斧头,然后FFT三次,结果发现有什么重复取一个啊,取的顺序啊牵扯出容斥啊,组合数什么的非常尴尬,然后一个半中午就玩没了,然后决定分开做,因为0太难搞了,可以视成很多种情况。

然后我就发现取一个和两个我很容易就可以搞,就是补0价值的斧头之后,FFT一次,然后可以发现,就有取a斧头和b斧头的顺序问题,那就除一下2

那么问题就转化为取三个斧头的情况了。

那就先自乘三次,就得到了斧头无限,顺序不同也算不同方案的方案数

首先先解决取了相同斧头的问题,那么ans在这个意义上是第1,2,3取的都不一样的方案 - 3*第1,2,3取的有两个一样的方案 + 2*第1,2,3取的有三个一样的方案(可能有人不理解这个方案为什么要*2,是因为比如取的是6,6,6,那它减了三次,实际上只需要减两次就好了。)

然后三个都一样的可以预处理吧,两个一样的就再跑一个FFT,怎么跑?想象一下,我们把两个相同的斧头捆在一起,然后让它和柿子再乘一次,就可以得到两个一样的情况。

接着就是顺序的问题了,三个都一样的没有这个问题不管ta,

有个栗子(打表推的):

比如4 5 6 8 ,得到18有10种

10:(5,5),(4,6),(6,4) 再+8

12:(6,6),(4,8),(8,4) 再+6

13:(5,8),(8,5) 再+5

14:(6,8),(8,6) 再+4

两个一样的情况就是(5,5,8)和(6,6,6)

分析一下,两个一样的情况有三种不同的组合,那就减掉

剩下就是都不一样的了,很容易发现有6种组合,那除一下,再把一样的加上

最后答案就是1/2的方案加3的方案了~

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
const double pi=acos(-1.0);

struct Complex
{
    double r,i;
    Complex(){r=0, i=0;}
    Complex(double _r,double _i){r=_r, i=_i;}
    friend Complex operator +(Complex x,Complex y){return Complex(x.r+y.r,x.i+y.i);}
    friend Complex operator -(Complex x,Complex y){return Complex(x.r-y.r,x.i-y.i);}
    friend Complex operator *(Complex x,Complex y){return Complex(x.r*y.r-x.i*y.i,x.r*y.i+x.i*y.r);}
}T[410000],A[410000],B[410000];
int d[410000],d2[410000],len;

int R[410000];
void fft(Complex *a,int n,int op)
{
    for(int i=0;i<n;i++)
        if(i<R[i])swap(a[i],a[R[i]]);
        
    for(int i=1;i<n;i*=2)
    {
        Complex wn(cos(pi/i),sin(pi*op/i));
        for(int j=0;j<n;j+=(i<<1))
        {
            Complex w(1,0);
            for(int k=0;k<i;k++,w=w*wn)
            {
                Complex a1=a[j+k],a2=a[j+k+i];
                a[j+k]  =a1+w*a2;
                a[j+k+i]=a1-w*a2;
            }
        }
    }
}

void FFT1(int n,int m)
{
    int nn=n,L=0;
    for(int i=0;i<=n;i++)A[i].r=B[i].r=T[i].r;
    m=m+n;for(n=1;n<=m;n*=2)L++;
    for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    
    fft(A,n,1);fft(B,n,1);
    for(int i=0;i<n;i++)A[i]=A[i]*B[i], B[i].r=0, B[i].i=0;
    
    fft(A,n,-1);
    for(int i=0;i<n;i++)
        A[i].r=int(A[i].r/n+0.5), A[i].i=0;
    
    for(int i=1;i<n;i++)A[i].r/=2;
    
    for(int i=0;i<n;i++)d[i]+=int(A[i].r), A[i].r=0, A[i].i=0;
    len=m;
    //第一次FFT,解决用1/2个不同物品组成不同价值
}
void FFT2(int n,int m)
{
    int nn=n,L=0;
    for(int i=0;i<=n;i++)A[i].r=B[i].r=T[i].r;
    m=m+n;for(n=1;n<=m;n*=2)L++;
    for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    
    fft(A,n,1);fft(B,n,1);
    for(int i=0;i<n;i++)A[i]=A[i]*B[i], B[i].r=0, B[i].i=0;
    
    fft(A,n,-1);
    for(int i=0;i<n;i++)
    {
        A[i].r=int(A[i].r/n+0.5), A[i].i=0;
//        if(int(A[i].r)!=0)printf("%d %d\n",i,int(A[i].r));
    }
//    printf("\n");


    n=nn;L=0;
    for(int i=0;i<=n;i++)B[i].r=T[i].r;
    m=m+n;for(n=1;n<=m;n*=2)L++;
    for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    
    fft(A,n,1);fft(B,n,1);
    for(int i=0;i<n;i++)A[i]=A[i]*B[i], B[i].r=0, B[i].i=0;
    
    fft(A,n,-1);
    for(int i=0;i<n;i++)
    {
        A[i].r=int(A[i].r/n+0.5), A[i].i=0;
//        if(int(A[i].r)!=0)printf("%d %d\n",i,int(A[i].r));
    }
//    printf("\n");
    
    for(int i=0;i<n;i++)d2[i]+=int(A[i].r), A[i].r=0, A[i].i=0;
    len=m;
    
//    for(int i=0;i<=len;i++)
    //    if(d[i]!=0)printf("%d %d\n",i,d[i]);
}
void FFT3(int n,int m)
{
    int nn=n,L=0;
    for(int i=0;i<=n;i++)A[i+i].r=B[i].r=T[i].r;
    
    m=m+n;for(n=1;n<=m;n*=2)L++;
    for(int i=0;i<n;i++)R[i]=(R[i>>1]>>1)|((i&1)<<(L-1));
    
/*    for(int i=0;i<n;i++)
        if(int(A[i].r)!=0)printf("%d %d\n",i,int(A[i].r));
    for(int i=0;i<n;i++)
        if(int(B[i].r)!=0)printf("%d %d\n",i,int(B[i].r));
*/        
    fft(A,n,1);fft(B,n,1);
    for(int i=0;i<n;i++)A[i]=A[i]*B[i], B[i].r=0, B[i].i=0;
    
    fft(A,n,-1);
    for(int i=0;i<n;i++)
        A[i].r=int(A[i].r/n+0.5), A[i].i=0;
        
//    for(int i=0;i<n;i++)
//        if(int(A[i].r)!=0)printf("%d %d\n",i,int(A[i].r));
        
    for(int i=0;i<n;i++)d2[i]-=int(A[i].r)*3, A[i].r=0, A[i].i=0;
    
    for(int i=1;i<=len;i++)
    {
        d2[i]/=6;d2[i]+=d[i];
        if(d2[i]!=0)
            printf("%d %d\n",i,d2[i]);
    }
}
int main()
{
//    freopen("f.in","r",stdin);
//    freopen("f.out","w",stdout);
    int n,x,mx=0;
    scanf("%d",&n);
    memset(d,0,sizeof(d));
    memset(d2,0,sizeof(d2));
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        T[x].r++;d2[x+x+x]+=2;
        mx=max(mx,x);
    }
    T[0].r=1;FFT1(mx,mx);
    T[0].r=0;FFT2(mx,mx);
    FFT3(mx*2,mx);
    return 0;
}

 

以上是关于bzoj3771: Triple的主要内容,如果未能解决你的问题,请参考以下文章

bzoj3771 Triple

[BZOJ3771]Triple

BZOJ3771Triple 生成函数+FFT

bzoj 3771 Triple FFT 生成函数+容斥

bzoj3771: Triple

bzoj3771 Triple