BZOJ 3513 idiots

题目传送门

分析:

FFT一手统计两根棍子相加的方案

然后一个值2S可能会被同一根S自己乘自己得到

然后要减去

其次,A+B和B+A会被算成两种方案,所以还要除以2

然后不太好算合法的方案数,但是非法的很好算

直接减去小于S的所有方案数乘以长度为S的棍子数就好了。。

疯狂卡常2333

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
 
#define maxn 500005
 
using namespace std;
 
inline int getint()
{
    int num=0,flag=1;char c;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
    while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
    return num*flag;
}
 
struct cp{
    double a,b;
    cp(){}
    cp(double x,double y){a=x,b=y;}
    friend cp operator +(cp x,cp y)
    {return cp(x.a+y.a,x.b+y.b);}
    friend cp operator -(cp x,cp y)
    {return cp(x.a-y.a,x.b-y.b);}
    friend cp operator *(cp x,cp y)
    {return cp(x.a*y.a-x.b*y.b,x.a*y.b+x.b*y.a);}
};
 
const double pi=acos(-1.0);
int k=1,bit;
cp a[maxn],b[maxn];
int rev[maxn];
long long num1[maxn],num2[maxn];
 
inline void fft(cp *a,int inv)
{
    for(int i=0;i<k;i++)if(i<rev[i])swap(a[i],a[rev[i]]);
    for(int mid=1;mid<k;mid<<=1)
    {
        cp tmp(cos(pi/mid),inv*sin(pi/mid));
        for(int i=0;i<k;i+=mid*2)
        {
            cp ret(1,0);
            for(int j=0;j<mid;j++,ret=ret*tmp)
            {
                cp x=a[i+j],y=ret*a[i+j+mid];
                a[i+j]=x+y,a[i+j+mid]=x-y;
            }
        }
    }
}
 
int main()
{
    int T=getint();
    while(T--)
    {
        memset(a,0,sizeof a);
        int mx=0;
        long long n=getint();k=1,bit=0;
        for(int i=0;i<n;i++)
        {
            int x=getint();mx=max(mx,x);
            a[x].a++;
        }
        while(k<(mx+1)*2-1)k<<=1;
        while((1<<bit)<k)bit++;
        for(int i=0;i<k;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(bit-1));
        for(int i=0;i<k;i++)b[i]=a[i];
        fft(a,1);
        for(int i=0;i<k;i++)a[i]=a[i]*a[i];
        fft(a,-1);
        for(int i=0;i<k;i++)
        {
            num1[i]=(long long)(b[i].a+0.5),
            num2[i]=(long long)(a[i].a/k+0.5);
            if(!(i&1))num2[i]-=num1[i>>1];
            num2[i]>>=1;
        }
        long long ans=(1ll*n*(n-1)*(n-2))/6;
        for(int i=1;i<=mx;i++)num2[i]+=num2[i-1],ans-=num2[i]*num1[i];
        printf("%.7lf
",1.0*ans*6/(n*(n-1)*(n-2)));
    }
}
View Code

原文地址:https://www.cnblogs.com/Darknesses/p/12064468.html