Q1(hdu1402):java
給出兩個很大的數字A,B,計算兩者乘積。ios
分析:這個題目java應該能過,用FFT作可以加速計算。這裏將字符串A按權(10進制)展開,前面的係數就是多項式的係數,這樣就構造出了多項式乘積形式,而後用FFT加速便可。優化
參考代碼以下:spa
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <math.h> using namespace std; const double PI = acos(-1.0); //複數結構體 struct complex { double r,i; complex(double _r = 0.0,double _i = 0.0) { r = _r; i = _i; } complex operator +(const complex &b) { return complex(r+b.r,i+b.i); } complex operator -(const complex &b) { return complex(r-b.r,i-b.i); } complex operator *(const complex &b) { return complex(r*b.r-i*b.i,r*b.i+i*b.r); } }; /* * 進行FFT和IFFT前的反轉變換。 * 位置i和 (i二進制反轉後位置)互換 * len必須去2的冪 */ 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]); //交換互爲小標反轉的元素,i<j保證交換一次 //i作正常的+1,j左反轉類型的+1,始終保持i和j是反轉的 k = len/2; while( j >= k) { j -= k; k /= 2; } if(j < k) j += k; } } /* * 作FFT * len必須爲2^k形式, * on==1時是DFT,on==-1時是IDFT */ 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].r /= len; } const int MAXN = 200010; complex x1[MAXN],x2[MAXN]; char str1[MAXN/2],str2[MAXN/2]; int sum[MAXN]; int main() { while(scanf("%s%s",str1,str2)==2) { int len1 = strlen(str1); int len2 = strlen(str2); int len = 1; while(len < len1*2 || len < len2*2)len<<=1; for(int i = 0;i < len1;i++) x1[i] = complex(str1[len1-1-i]-'0',0); for(int i = len1;i < len;i++) x1[i] = complex(0,0); for(int i = 0;i < len2;i++) x2[i] = complex(str2[len2-1-i]-'0',0); for(int i = len2;i < len;i++) x2[i] = complex(0,0); //求DFT 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); for(int i = 0;i < len;i++) sum[i] = (int)(x1[i].r+0.5); for(int i = 0;i < len;i++) { sum[i+1]+=sum[i]/10; sum[i]%=10; } len = len1+len2-1; while(sum[len] <= 0 && len > 0)len--; for(int i = len;i >= 0;i--) printf("%c",sum[i]+'0'); printf("\n"); } return 0; }
Q2(hdu4609):code
給出一個n元素整數集合,問任意取出三個元素,組成三角形的機率。blog
分析:這裏只須要計數整數集合中可以組成多少個三角形便可,而後除以C(n , 3).對於如何計數可以組成多少個三角形,純暴力時間上是O(n^3),須要優化。字符串
參考代碼以下:string
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <math.h> using namespace std; const double PI = acos(-1.0); struct Complex { double r,i; Complex(double _r = 0,double _i = 0) { r = _r; i = _i; } Complex operator +(const Complex &b) { return Complex(r+b.r,i+b.i); } Complex operator -(const Complex &b) { return Complex(r-b.r,i-b.i); } Complex operator *(const Complex &b) { return Complex(r*b.r-i*b.i,r*b.i+i*b.r); } }; 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].r /= len; } const int MAXN = 400040; Complex x1[MAXN]; int a[MAXN/4]; long long num[MAXN];//100000*100000會超int long long sum[MAXN]; int main() { int T; int n; scanf("%d",&T); while(T--){ scanf("%d",&n); memset(num,0,sizeof(num)); for(int i = 0;i < n;i++){ scanf("%d",&a[i]); num[a[i]]++; } sort(a,a+n); int len1 = a[n-1]+1; int len = 1; while( len < 2*len1 )len <<= 1; for(int i = 0;i < len1;i++) x1[i] = Complex(num[i],0); for(int i = len1;i < len;i++) x1[i] = Complex(0,0); fft(x1,len,1); for(int i = 0;i < len;i++) x1[i] = x1[i]*x1[i]; fft(x1,len,-1); for(int i = 0;i < len;i++) num[i] = (long long)(x1[i].r+0.5); len = 2*a[n-1];//爲105行代碼的處理優化一下界 for(int i = 0;i < n;i++) {num[a[i]+a[i]]--;} //減掉取兩個相同的組合 for(int i = 1;i <= len;i++) {num[i]/=2;} //選擇的無序,除以2 sum[0] = 0; for(int i = 1;i <= len;i++){sum[i] = sum[i-1]+num[i];}//卷積的前綴和 long long cnt = 0;//可以組成三角形的個數 for(int i = 0;i < n;i++){ cnt += sum[len]-sum[a[i]]; cnt -= (long long)(n-1-i)*i; //減掉一個取大,一個取小的 cnt -= (n-1); //減掉一個取自己,另一個取其它 cnt -= (long long)(n-1-i)*(n-i-2)/2;//減掉大於它的取兩個的組合 } //總數 long long tot = (long long)n*(n-1)*(n-2)/6;//答案几率的分母C(n , 3) printf("%.7lf\n",(double)cnt/tot); } return 0; }
Q3(hdu5885):it
給出一個n*m的格點,每一個格點有一個權值。給定一個半徑r,對於某個點(x,y),他周圍到他的歐幾里得距離小於r的格點,都會對(x,y)產生一個貢獻度。那麼問在這個n*m格點圖中,得到最大貢獻度的格點的貢獻度是多少。io
參考代碼以下:
#include <stdio.h> #include <iostream> #include <string.h> #include <algorithm> #include <math.h> using namespace std; const double PI = acos(-1.0); struct Complex { double r,i; Complex(double _r = 0,double _i = 0) { r = _r; i = _i; } Complex operator +(const Complex &b) { return Complex(r+b.r,i+b.i); } Complex operator -(const Complex &b) { return Complex(r-b.r,i-b.i); } Complex operator *(const Complex &b) { return Complex(r*b.r-i*b.i,r*b.i+i*b.r); } }; 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].r /= len; } const int MAXN = (1 << 21); Complex x1[MAXN] , x2[MAXN]; //一開始兩個多項式的係數向量,通過fft以後記錄多項式在單位複數根處的值 double dis(int x , int y){ return sqrt(x*x + y*y); } int main() { int n , m , R; double r; while(~scanf("%d%d%lf" , &n , &m , &r)){ R = ceil(r); int M = m + 2 * R; int len = 1;//len是兩個多項式的界 while( len < M * M )len <<= 1; for(int i = 0;i < len;++i) x1[i] = x2[i] = Complex(0 , 0);//初始化一下 //獲得第一個多項式的係數向量 for(int i = 0;i < n;i++){ for(int j = 0;j < n;j++){ int p;scanf("%d" , &p); x1[i * M + j] = Complex(p , 0); } } //獲得第二個多項式的係數向量 for(int i = -R;i <= R;++i){ for(int j = -R;j <= R;++j){ if(dis(i , j) < r) x2[(i+R)*M + j + R] = Complex(1.0/(dis(i , j) + 1), 0); } } fft(x1 , len , 1); fft(x2 , len , 1); //獲得兩個的多項式在單位複數根處的值,用x1[],x2[]存儲 for(int i = 0;i < len;i++) x1[i] = x1[i] * x2[i]; //獲得多項式乘法的點值表達 fft(x1 , len , -1);//逆DFT將點值表達轉化成係數表達 double ans = 0; for(int i = 0;i < n;++i) for(int j = 0;j < m;++j) ans = max(ans , x1[(i+R)*M + (j + R)].r); printf("%.3lf\n" , ans); } return 0; }