zoukankan      html  css  js  c++  java
  • 【hdu4609】 3-idiots FFT

    题外话:好久没写blog了啊~~

    题目传送门

    题目大意:给你m条长度为ai的线段,求在其中任选三条出来,能构成三角形的概率。即求在这n条线段中找出三条线段所能拼出的三角形数量除以$inom{m}{3}$。

    假设我们手中有3条长度分别为$x,y,z$的边(为了简化问题我们假设$x<y<z$,$x,y,z$相等的情况另行讨论),如果他们能拼成三角形,必然满足$x+y>z$且$z-y<x$。

    该题的$O(m^3)$做法:枚举其中的3条边,套用上面的判断公式,进行累计。

    但通过简单的变式,我们假设我们已经确定了$x$和$y$,那么$z$的范围即为$[y,x+y)$,我们维护一个数组$num$,$num_i$表示长度为i的线段数量。再维护一个num的前缀和sum。则$ans=sum^{n-1}_{x=1}  sum^{n}_{y=x+1} num_x * num_y*(sum[x+y-1]-sum[y])$,运算该和式的时间复杂度为$O(n^2)$,此处的n表示最长线段的长度。

    然而还是会TLE。。。。

    我们考虑对其做一些变式。

    $ans=sum^{n-1}_{x=1}  sum^{n}_{y=x+1} num_x * num_y*(sum[x+y-1]-sum[y])$

     $=sum^{n-1}_{x=1}  sum^{n}_{y=x+1} num_x * num_y*sum[x+y-1] -sum^{n-1}_{x=1}  sum^{n}_{y=x+1} num_x * num_y*sum[y]$

    该式子的后半部分,我们可以通过维护$num_y*sum[y]$的后缀和,实现O(n)的计算。

    下面我们继续对式子的前半部分变式。令t=x+y。则有

    $=frac{1}{2}sum^{2n}_{t=2}  sum^{t-1}_{p=1} num_p*num_{t-p}*sum[t-1] -frac{1}{2}sum^{n}_{i=1}num_i^2*sum[2*i-1]$

    我们发现,该式子的$sum_{p=1}^{t-1} num_{p} imes num_{t-p}$,可以用FFT求出。则时间复杂度成功降低至O(n log n),而式子的后半部分可以O(n)求出,求和时间复杂度降低至O(n log n)。

    下面说下x=y=z,x=y<z,x<y=z的处理方法。

    x=y=z:$sum _{forall num_x>2} inom{num_x}{3}$

    x=y<z: $sum _{x=1}^{n} inom{num_x}{2}*(sum[2x-1]-sum[x])$

    x<y=z:$sum_{x=1}^{n}(sumc2_n-sumc2_x)$ ,其中$sumc2_x$ 表示$sum_{i=1}^{x}inom{num_x}{2}$。

    把这三种情况和最初描述的情况相加即可。

     1 #include<bits/stdc++.h>
     2 #define M 270000
     3 #define cp complex<double>
     4 #define PI acos(-1)
     5 #define L long long
     6 using namespace std;
     7 cp a[M];
     8 void change(cp a[],int len){
     9     for(int i=0,j=0;i<len-1;i++){
    10         if(i<j) swap(a[i],a[j]);
    11         int k=len>>1;
    12         while(j>=k) j-=k,k>>=1;
    13         j+=k;
    14     }
    15 }
    16 void fft(cp a[],int n,int on){
    17     change(a,n); cp t,u;
    18     for(int h=2;h<=n;h<<=1){
    19         cp wn(cos(-on*2*PI/h),sin(-on*2*PI/h));
    20         for(int j=0;j<n;j+=h){
    21             cp w(1,0);
    22             for(int k=j;k<j+(h>>1);k++){
    23                 u=a[k]; t=w*a[k+(h>>1)];
    24                 a[k]=u+t; a[k+(h>>1)]=u-t;
    25                 w=w*wn;
    26             }
    27         }
    28     }
    29 }
    30 L num[M]={0},sum[M]={0},sumhh[M]={0},sumc2[M]={0},sc[M]={0},ans=0;
    31 
    32 int Main(){
    33     int m,n,maxn=0; scanf("%d",&n);
    34     for(int i=1;i<=n;i++){
    35         int x; scanf("%d",&x);
    36         num[x]++; maxn=max(maxn,x);
    37     }
    38     for(m=1;m<(maxn*2+1);m<<=1);
    39     for(int i=0;i<m;i++) a[i]=cp(num[i],0);
    40     fft(a,m,1);
    41     for(int i=0;i<m;i++) a[i]=a[i]*a[i];
    42     fft(a,m,-1);
    43     for(int i=0;i<m;i++) sc[i]=(a[i+1].real()+0.5)/m;
    44     for(int i=1;i<m;i++){
    45         sum[i]=sum[i-1]+num[i];
    46         sumc2[i]=sumc2[i-1]+num[i]*(num[i]-1)/2;
    47     }
    48     
    49     L ans1=0,ans2=0,ans3=0,ans4=0;
    50     for(int i=1;i<=maxn;i++) if(num[i]>2) 
    51     ans1+=(L)num[i]*(num[i]-1)*(num[i]-2);
    52     ans1/=6;//x=y=z
    53     for(int i=1;i<=maxn;i++) 
    54     ans2+=(L)num[i]*(num[i]-1)*(sum[2*i-1]-sum[i]);
    55     ans2/=2;//x=y<z
    56     for(int i=1;i<=maxn;i++)
    57     ans3+=num[i]*(sumc2[maxn]-sumc2[i]);
    58     //x<y=z
    59     for(int i=1;i<2*maxn;i++) ans4+=sum[i]*sc[i];
    60     for(int i=1;i<=maxn;i++) ans4-=num[i]*num[i]*sum[2*i-1];
    61     ans4/=2;//x<y<z卷积部分
    62     
    63     for(int i=maxn;i;i--) sumhh[i]=sumhh[i+1]+num[i]*sum[i];
    64     for(int i=1;i<maxn;i++) ans4-=num[i]*sumhh[i+1];
    65     ans=ans1+ans2+ans3+ans4;
    66     
    67     double fenmu=(L)n*(n-1)*(n-2)/6;
    68     double hh=ans/fenmu;
    69     printf("%.7lf
    ",hh);
    70 }
    71 int main(){
    72     //freopen("in.txt","r",stdin);
    73     //freopen("out.txt","w",stdout);
    74     int cas; scanf("%d",&cas);
    75     while(cas--){
    76         memset(num,0,sizeof(num)); memset(sum,0,sizeof(sum));
    77         memset(sumhh,0,sizeof(sumhh)); memset(sumc2,0,sizeof(sumc2));
    78         memset(sc,0,sizeof(sc)); memset(a,0,sizeof(a)); ans=0;
    79         Main();
    80     }
    81 }
  • 相关阅读:
    正则表达式进阶——你从没想过的正则表达式使用方式
    bert 预训练模型路径
    Ubuntu 18.04 美化
    循环中存在异步的情况
    SQL truncate 、delete与drop区别
    用express创建网站出现"$ DEBUG=microbog ./bin/www"的提示
    全局安装了express框架,但是无法使用express指令的问题
    jQuery获取textarea中的文本
    js实现查询关键词,使其高亮
    SQL Server常用函数汇总
  • 原文地址:https://www.cnblogs.com/xiefengze1/p/8214112.html
Copyright © 2011-2022 走看看