【BZOJ 1128】数列
题目大意:
对于一个长度为$n$的数列$p$,数列中任意两个数互质。
现在有一个无限长的储存器,从$p_1$开始,把储存器中$p_1$倍数位置都赋值为$p_1$,把储存器中$p_2$倍数位置都赋值为$p_2$,把储存器中$p_3$倍数位置都赋值为$p_3$,以此类推。
求每个$p_i$在储存器中出现的比例,用分数表示。
$n \le 1000$、$p \le 10^9$,需要用到高精度。
分析:
初步推导公式:
根据题意,前面赋值过的数会被后面的数覆盖,所以可以从后往前推,那些被覆盖过的就忽略掉。
易知,这些数在存储器中是循环出现的,周期为所有$p$的乘积,那么只要考虑区间$[1,\prod_{i=1}^{n}p_i]$内的数就好了。
对于每个数$p$,在这个区间内是$p_i$倍数的数的个数为$\frac{\prod_{i=1}^{n}p_i}{p}$,占所有数的比例为$\frac{1}{p}$,然而还要减去那些会被后面覆盖的。而在被覆盖的数中,被$p_i$整除的数也是平均分布出现的。
设$p_i$在存储器中出现的比例为$A_i$,那么就得到:
$$A_i=\frac{1}{p} - \frac{1}{p}\cdot \sum_{j=i+1}^{n}A_j = \frac{1}{p}(1-\sum_{j=i+1}^{n}A_j)$$
这样,就可以用一个$Sum$记录下$A_i$的后缀和,就可以线性推导公式了。
最简分数形式:
但是,仍然存在一个问题,那就是题目中要求的最简分数形式。
取最简分数形式有两种方法,第一是找两个高精度数的$GCD$,第二种是枚举每个$p$看看能不能除。
然而在高精度的情况下这两种方法都不可取,将高精度数的长度看作$n$,则不论哪种方法每次对一个分数进行简化就需要$O(n^2)$的复杂度,接合线性的推导,就需要$O(n^3)$的复杂度,显然会T。
进一步推导:
因为要用到$A_i$的后缀和,那么不妨再设$S_i=\sum_{j=i+1}^{n}A_j$。
然后就有:
$$\begin{array}\\A_i&=&\frac{1-S_i}{p_i} \\S_{i-1} &=& S_i + A_i \\ A_{i-1}&=&\frac{1-S_{i-1}}{p_{i-1}} \\ &=&\frac{1-S_i-A_i}{p_{i-1}} \\ &=&\frac{A_i\cdot p_i-A{i}}{p_{i-1}} \\ &=&A_i\cdot \frac{p_i-1}{p_{i-1}} \end{array}$$
即:
$$\left \{ \begin{array} \\A_n&=&\frac{1}{p_n} \\A_i&=&A_{i+1}\cdot\frac{p_{i+1}-1}{p_i}\ (i \lt n) \end{array} \right .$$
这样就得到了另一个等价的递推式,而这个递推式的优点可以每次乘的同时进行分数化简,只要对一个高精度数和一个单精度数取$GCD$,或对两个单精度数取$GCD$,这样$GCD$的复杂度就变成了$O(n)$,整体复杂度为$O(n^2)$。
代码:
1 #include <cstdio> 2 #include <cstring> 3 4 #define BNmod 1000000000ll 5 6 struct BigNumber 7 { 8 long long m[1010]; 9 10 inline long long operator %(long long x) 11 { 12 long long last = 0; 13 for (int i = 1009; i >= 0; i--) 14 { 15 last = last + m[i]; 16 last = last % x * BNmod; 17 } 18 return last / BNmod; 19 } 20 21 inline BigNumber operator *(long long x) 22 { 23 BigNumber Tmp; 24 long long last = 0; 25 for (int i = 0; i <= 1009; i++) 26 { 27 Tmp.m[i] = last + m[i] * x; 28 last = 0; 29 if (Tmp.m[i] >= BNmod) 30 { 31 last = Tmp.m[i] / BNmod; 32 Tmp.m[i] %= BNmod; 33 } 34 } 35 return Tmp; 36 } 37 38 inline BigNumber operator /(long long x) 39 { 40 BigNumber Tmp; 41 long long last = 0; 42 for (int i = 1009; i >= 0; i--) 43 { 44 Tmp.m[i] = last + m[i]; 45 last = Tmp.m[i] % x * BNmod; 46 Tmp.m[i] /= x; 47 } 48 return Tmp; 49 } 50 51 inline void Print() 52 { 53 int flag = 0; 54 for (int i = 1009; i >= 0; i--) 55 { 56 if (flag == 0 && m[i] > 0) 57 { 58 flag = 1; 59 printf("%lld", m[i]); 60 } 61 else 62 { 63 if (flag == 1) 64 { 65 printf("%09lld", m[i]); 66 } 67 } 68 } 69 if (flag == 0) putchar('0'); 70 } 71 } FZ, FM, ans[1010][2]; 72 73 int n, p[1010]; 74 75 long long gcd(long long a, long long b) 76 { 77 return b ? gcd(b, a % b) : a; 78 } 79 80 long long gcd(BigNumber a, long long b) 81 { 82 return b ? gcd(b, a % b) : 1; 83 } 84 85 int main() 86 { 87 scanf("%d", &n); 88 for (int i = 1; i <= n; i++) 89 { 90 scanf("%d", &p[i]); 91 } 92 memset(FZ.m, 0, sizeof(FZ.m)); 93 memset(FM.m, 0, sizeof(FM.m)); 94 FZ.m[0] = 1, FM.m[0] = p[n]; 95 ans[n][0] = FZ; 96 ans[n][1] = FM; 97 for (int i = n - 1; i > 0; i--) 98 { 99 int a = p[i + 1] - 1, b = p[i]; 100 int d = gcd(a, b); a /= d, b /= d; 101 d = gcd(FZ, b); FZ = FZ / d; b /= d; 102 d = gcd(FM, a); FM = FM / d; a /= d; 103 FZ = FZ * a; FM = FM * b; 104 int f = 0; 105 for (int j = 0; j < 1010; j++) 106 f += (FZ.m[j] > 0); 107 if (f == 0) 108 { 109 memset(FM.m, 0, sizeof(FM.m)); 110 FM.m[0] = 1; 111 } 112 ans[i][0] = FZ; ans[i][1] = FM; 113 } 114 for (int i = 1; i <= n; i++) 115 { 116 ans[i][0].Print(); 117 printf("/"); 118 ans[i][1].Print(); 119 printf("\n"); 120 } 121 }