BZOJ[3771]Triple 生成函数+容斥原理

传送门ber~

构造出原序列的生成函数 A ,它的三次方就是损失的方案数
可惜题目要求方案互不相同,这样计算会有重复的方案出现
那我们可以容斥一发
A 表示所有物品选一个的生成函数, B 表示一次选俩的生成函数, C 表示一次选仨的生成函数
手动容斥一发
拿一个的方案数: A
拿两个的方案数: ( A A B ) / 2 A A 就是带重复的选两个,重复一共有两种:第一种是两个一样的拼到一起,减去 B 就可以解决,还有一种就是将 x + y y + x 算了两次,除 2 就OK
拿三个的方案数: ( A 3 3 A B + 2 C ) / 6 ,这个和上一个差不多,感性理解下就可以了(实在不行画个图模拟一下乘法的过程)

代码如下:

#include<algorithm>
#include<ctype.h>
#include<cstdio>
#include<cmath>
#define N 550050
using namespace std;
const double DFT=2.0,IDFT=-2.0;
const double pi=acos(-1);
inline int read(){
    int x=0,f=1;char c;
    do c=getchar(),f=c=='-'?-1:f; while(!isdigit(c));
    do x=(x<<3)+(x<<1)+c-'0',c=getchar(); while(isdigit(c));
    return x*f;
}
struct Complex{
    double x,y;
    Complex(){}
    Complex(double _,double __):x(_),y(__){}
    Complex operator + (Complex b) const{return Complex(x+b.x,y+b.y);}
    Complex operator - (Complex b) const{return Complex(x-b.x,y-b.y);}
    Complex operator * (Complex b) const{return Complex(x*b.x-y*b.y,x*b.y+y*b.x);}
    Complex operator * (double b) const{return Complex(x*b,y*b);}
    Complex operator / (double b) const{return Complex(x/b,y/b);}
}a[N],b[N],c[N],d[N];
int pos[N];
int n,x,len,maxx;
inline void FFT(Complex a[],double mode){
    for(int i=0;i<len;i++)
        if(i<pos[i])
            swap(a[i],a[pos[i]]);
    for(int i=2,mid=1;i<=len;i<<=1,mid<<=1){
        Complex wm(cos(2.0*pi/i),sin(mode*pi/i));
        for(int j=0;j<len;j+=i){
            Complex w(1,0);
            for(int k=j;k<j+mid;k++,w=w*wm){
                Complex l=a[k],r=w*a[k+mid];
                a[k]=l+r;a[k+mid]=l-r;
            }
        }
    }
    if(mode==IDFT)
        for(int i=0;i<len;i++)
            a[i].x/=len;
    return;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++){
        x=read();
        a[x].x++;b[x*2].x++;c[x*3].x++;
        maxx=max(maxx,x*3);
    }
    for(len=1;len<maxx<<1;len<<=1);
    for(int i=0;i<len;i++){
        pos[i]=pos[i>>1]>>1;
        if(i&1) pos[i]|=len>>1;
    }
    FFT(a,DFT);FFT(b,DFT);FFT(c,DFT);
    for(int i=0;i<len;i++)
        d[i]=a[i]+(a[i]*a[i]-b[i])/2+(a[i]*a[i]*a[i]-a[i]*b[i]*3+c[i]*2)/6;
    FFT(d,IDFT);
    for(int i=1;i<=len;i++){
        int k=int(d[i].x+0.1);
        if(k) printf("%d %d\n",i,k);
    }
return 0;
}

猜你喜欢

转载自blog.csdn.net/waduan2/article/details/79541682