选木棍构成三角形(FFT)

 题目要求,给N个木棍,从中选3个,问能构成三角形的概率多大。

N<= 1e5.

木棍长度<=1e5.

 

注意事项:

     直接把len开成1e5会TLE。

     统计答案的时候可能会爆int。

     删除非法情况。

#include 
#define ll long long
using namespace std;
const double PI = acos(-1.0);
struct Complex  {
    double x, y;
    Complex(double _x=0.0,double _y = 0.0) {
        x = _x;
        y = _y;
    }
    Complex operator -(const Complex &b) const {
        return Complex(x-b.x, y-b.y);
    }
    Complex operator +(const Complex &b) const {
        return Complex(x+b.x, y+b.y);
    }
    Complex operator *(const Complex &b) const {
        return Complex(x*b.x-y*b.y, x*b.y+y*b.x);
    }
};
void change(Complex y[], int len) {
    int i,j,k;
    for(i = 1, j = len/2; i < len-1; i++) {
        if(i < j) swap(y[i],y[j]);
        k = len/2;
        while(j >= k) {
            j -= k;
            k /= 2;
        }
        if(j < k) j += k;
    }
}
void fft(Complex y[], int len, int on) {
    change(y, len);
    for(int h = 2; h <= len; h <<= 1) {
        Complex wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
        for(int j = 0; j <= len; j+= h) {
            Complex w(1,0);
            for(int k = j; k < j+h/2; k++) {
                Complex u = y[k];
                Complex t = w*y[k+h/2];
                y[k] = u+t;
                y[k+h/2] = u-t;
                w = w*wn;
            }
        }
    }
    if(on == -1)
        for(int i = 0; i < len; i++)
            y[i].x /= len;
}
const int N = 1e5+100;
Complex x1[8*N],x2[8*N];
ll a[N],num[N],b[N];
vectorq;
ll sum[8*N];
int main() {
    freopen("a.txt","r",stdin);
    //ios::sync_with_stdio(0);
    int T;
    scanf("%d",&T);
    while(T--) {
        int n;
        scanf("%d",&n);
        memset(num,0,sizeof(num));
        q.clear();
        ll maxx = 0;                    //记录最大值,防TLE
        for(int i = 1; i <= n; i++) {
            ll x;
            scanf("%lld",&x);
            maxx = max(maxx,x);
            num[x]++;
            q.push_back(x);
        }
        int len1 = maxx+1;      
        int len = 1;
        int len2 = maxx+1;
        for(int i = 0; i < len1; i++) {
            a[i] = num[i];
            b[i] = num[i];
        }
        while(len < len1*2 || len < len2*2) len <<= 1;
        for(int i = 0; i < len1; i++) x1[i] = Complex(a[i],0);
        for(int i = len1; i < len; i++) x1[i] = Complex(0,0);
        for(int i = 0; i < len2; i++) x2[i] = Complex(b[i],0);
        for(int i = len2; i < len; i++) x2[i] = Complex(0,0);
        fft(x1,len,1);
        fft(x2,len,1);
        for(int i = 0; i < len; i++) x1[i] = x1[i]*x2[i];
        fft(x1,len,-1);
        len = len1+len2-1;                  //这才是有用的len
        for(int i = 0; i < len; i++) {
            sum[i] = (ll)(x1[i].x+0.5);
        }
        for(auto x:q) {
            sum[2*x]--;                     //减去取两个编号相同的
        }
        for(int i = 0; i < len; i++) sum[i]/=2;             //(i,j)和(j,i)重复
        for(int i = 1; i < len; i++) sum[i] += sum[i-1];    //sum[i]表示取两个木棍,长度之和<=i的取法

        sort(q.begin(),q.end());
        ll ans = 0;
        for(int i = 0; i < n; i++) {     //枚举最长边c
           ans += sum[len-1]-sum[q[i]];  //选两条边使得 a+b>c
           ans -= (ll)(i)*(n-i-1);       //减去a,b中一个大于c一个小于c的情况
           ans -= (ll)(n-i-1)*(n-i-2)/2; //减去a,b均大于c的情况
           ans -= (n-1);                 //减去a,b中有一个选c的情况
        }
        double p = ans*6.0/(1.0*n*(n-1)*(n-2)); //除以总选法
        printf("%.7f\n",p);
    }
    return 0;
}

 

你可能感兴趣的:(FFT)