【hdu4609】 3-idiots FFT
题外话:好久没写blog了啊~~
题目大意:给你m条长度为ai的线段,求在其中任选三条出来,能构成三角形的概率。即求在这n条线段中找出三条线段所能拼出的三角形数量除以(m3)。
假设我们手中有3条长度分别为x,y,z的边(为了简化问题我们假设x<y<z,x,y,z相等的情况另行讨论),如果他们能拼成三角形,必然满足x+y>z且z−y<x。
该题的O(m3)做法:枚举其中的3条边,套用上面的判断公式,进行累计。
但通过简单的变式,我们假设我们已经确定了x和y,那么z的范围即为[y,x+y),我们维护一个数组num,numi表示长度为i的线段数量。再维护一个num的前缀和sum。则ans=∑n−1x=1∑ny=x+1numx∗numy∗(sum[x+y−1]−sum[y]),运算该和式的时间复杂度为O(n2),此处的n表示最长线段的长度。
然而还是会TLE。。。。
我们考虑对其做一些变式。
ans=∑n−1x=1∑ny=x+1numx∗numy∗(sum[x+y−1]−sum[y])
=∑n−1x=1∑ny=x+1numx∗numy∗sum[x+y−1]−∑n−1x=1∑ny=x+1numx∗numy∗sum[y]
该式子的后半部分,我们可以通过维护numy∗sum[y]的后缀和,实现O(n)的计算。
下面我们继续对式子的前半部分变式。令t=x+y。则有
=12∑2nt=2∑t−1p=1nump∗numt−p∗sum[t−1]−12∑ni=1num2i∗sum[2∗i−1]
我们发现,该式子的$\sum_{p=1}^{t-1} num_{p}\times 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:∑∀numx>2(numx3)
x=y<z: ∑nx=1(numx2)∗(sum[2x−1]−sum[x])
x<y=z:∑nx=1(sumc2n−sumc2x) ,其中sumc2x 表示∑xi=1(numx2)。
把这三种情况和最初描述的情况相加即可。
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\n",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 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!