【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$。

然后就有:

$$egin{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_icdot p_i-A{i}}{p_{i-1}} \ &=&A_icdot frac{p_i-1}{p_{i-1}} end{array}$$

即:

$$left { egin{array} \A_n&=&frac{1}{p_n} \A_i&=&A_{i+1}cdotfrac{p_{i+1}-1}{p_i} (i lt n) end{array} ight .$$

这样就得到了另一个等价的递推式,而这个递推式的优点可以每次乘的同时进行分数化简,只要对一个高精度数和一个单精度数取$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("
");
120     }
121 }
原文地址:https://www.cnblogs.com/lightning34/p/4603907.html