题意:
输入n,求c(n,0)到c(n,n)的所有组合数的最小公倍数。
输入:
首行输入整数t,表示共有t组测试样例。
每组测试样例包含一个正整数n(1<=n<=1e6)。
输出:
输出结果(mod 1e9+7)。
感觉蛮变态的,从比赛开始我就是写的这道题,比赛结束还是没写出来……
期间找到了逆元,最小公倍数,组合数的各种公式,但是爆了一下午tle。
比赛结束,题解告诉我,公式秒杀法……
但是公式看不懂,幸好有群巨解说,所以有些听懂了,但还是需要继续思考才能弄懂。
题解:
设ans[i]表示i的所有组合数的最小公倍数。
设f[i]表示从1到i的正整数的最小公倍数。
然后获得从f[i]到ans[i]的公式——ans[i] = f[i+1]/i。 anss[i-1] = (f[i]*inv[i])%Mod;
然后获得求f[i]的公式——
if(i == p^k) f[i] = f[i-1]*p;
else f[i] = f[i-1];
接下来需要做的就是找到那些i == p^k了。
解题步骤:
1. 打素数表;
2. 由素数表寻找i == p^k;
3. f[1] = 1,打f[]数组表和ans[]数组表;
4. 输入数据;
5. 根据输入数据和ans[]表输出答案。
我认为还有其他方法,因为ac的程序的运行时间从15ms到900+ms都有。更多方法持续寻找中。
我的(根据题解的)代码——
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 7 #define LL long long 8 const int N = 1000010; 9 const int Mod = 1000000007; 10 11 int n, t; 12 LL ans, mid; 13 LL inv[N]; 14 LL anss[N]; 15 bool su2[N]; 16 int su[N], su1[N]; 17 LL f[N]; //f[i]表示1~i的最小公倍数 18 19 20 void table() 21 { 22 inv[1] = 1; 23 for(int i = 2; i < N; i++) //求i的逆元 24 { 25 inv[i] = inv[Mod%i]*(Mod-Mod/i) % Mod; 26 } 27 28 memset(su2, 1, sizeof(su2)); 29 memset(su1, 0, sizeof(su1)); 30 su2[0] = su2[1] = 0; //求N以内的素数 31 su2[2] = 1; 32 for(int i = 3; i < N; i++) su2[i] = i%2 == 0 ? 0 : 1; 33 for(int i = 3; i <= sqrt(N*1.0); i++) 34 { 35 if(su2[i]) 36 { 37 for(int j = i*i; j <= N; j += 2*i) 38 { 39 su2[j] = 0; 40 } 41 } 42 } 43 int k = 0; 44 for(int i = 0; i < N; i++) //打N以内的素数表 45 { 46 if(su2[i] == 1) su[k++] = i; 47 } 48 for(int i = 0; i < k; i++) //寻找满足p^k的数,其中p为素数,k为正整数 49 { 50 LL mid = su[i]; 51 while(mid < N) 52 { 53 su1[mid] = su[i]; 54 mid *= su[i]; 55 } 56 } 57 f[1] = 1; //打N以内的1~i的最小公倍数表 58 for(int i = 2; i < N; i++) 59 { 60 if(su1[i]) f[i] = f[i-1]*su1[i]; 61 else f[i] = f[i-1]; 62 f[i] %= Mod; 63 anss[i-1] = (f[i]*inv[i])%Mod; //答案表 64 } 65 } 66 67 int main() 68 { 69 //freopen("test.in", "r", stdin); 70 //freopen("test.out", "r", stdout); 71 table(); 72 scanf("%d", &t); 73 while(t--) 74 { 75 scanf("%d", &n); 76 printf("%I64d\n", anss[n]); 77 } 78 return 0; 79 }
时间: 2024-10-14 23:19:04