【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 }
时间: 2024-11-11 15:56:53

【BZOJ 1128】数列的相关文章

BZOJ 4305: 数列的GCD( 数论 )

对于d, 记{ai}中是d的倍数的数的个数为c, 那么有: 直接计算即可,复杂度O(NlogN+MlogM) --------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const in

【BZOJ】【1485】【HNOI2009】有趣的数列

Catalan数/组合数取模 Aha!这题我突然灵光一现就想到Catalan数……就是按顺序安排1~2n这些数(以满足前两个条件)……分配到奇数位置上的必须比偶数位置上的多(要不就不满足第三个条件了) Catalan数可以用C(n,2n)/(n+1)直接求 但是这题P不保证是质数感觉很捉急啊= =不会捉啊……然后我也没想到50分的DP,果断滚粗了啊sad QAQ Orz zyf & 盾爷,搬运题解: 假设现在我对于数字 i ,要把他的 j 次方加到答案中去,若k是 i 的一个质因子,那么我只要把

BZOJ 1500: [NOI2005]维修数列

1500: [NOI2005]维修数列 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 12880  Solved: 4112[Submit][Status][Discuss] Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目.第2行包含N个数字,描述初始时的数列.以下M行,每行一条命令,格式参见问题描述中的表格.任何时刻数列中最多含有500 000个数,

BZOJ 1005 明明的烦恼(Prufer数列)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1005 题意:给出一棵树的某些节点的度数d,有些未给.问满足这个条件的树有多少种? 思路:(1)Prufer 数列是无根树的一种数列.由一棵树可以构造出一个Prufer数列,Prufer数列可转化为原来的树.由树生成Prufer的一种简单方法是每次找出标 号最小的叶子节点将其父节点添加到Prufer数列并将该叶子节点删除.直到最后只剩下两个节点时结束.比如下面的这个树按照我们刚才的方法生

【BZOJ 1005】无根树的Prufer数列

今天看了Prufer数列这个东西. 每一个Prufer数列和无根树是一一对应的.所以求出有多少符合要求的Prufer数列即可. 点i在Prufer数列中的出现次数为i的度数 - 1. 代码如下:[用分解质因数的方法,避免了高精除] #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <iostream> using namesp

【BZOJ】[HNOI2009]有趣的数列

[算法]Catalan数 [题解] 学了卡特兰数就会啦>_<! 因为奇偶各自递增,所以确定了奇偶各自的数字后排列唯一. 那么就是给2n个数分奇偶了,是不是有点像入栈出栈序呢. 将做偶数标为-1,做奇数标为+1,显然当偶数多于奇数时不合法,因为它压不住后面的奇数. 然后其实这种题目,打表就可知啦--QAQ 然后问题就是求1/(n+1)*C(2n,n)%p了,p不一定是素数. 参考bzoj礼物的解法. 看到网上清一色的素数筛+分解质因数解法,不解了好久,感觉写了假的礼物-- 后来觉得礼物的做法才比

【BZOJ 4305】 4305: 数列的GCD (数论)

4305: 数列的GCD Description 给出一个长度为N的数列{a[n]},1<=a[i]<=M(1<=i<=N). 现在问题是,对于1到M的每个整数d,有多少个不同的数列b[1], b[2], ..., b[N],满足: (1)1<=b[i]<=M(1<=i<=N): (2)gcd(b[1], b[2], ..., b[N])=d: (3)恰好有K个位置i使得a[i]<>b[i](1<=i<=N) 注:gcd(x1,x2,

bzoj 4636: 蒟蒻的数列

4636: 蒟蒻的数列 Description 蒟蒻DCrusher不仅喜欢玩扑克,还喜欢研究数列 题目描述 DCrusher有一个数列,初始值均为0,他进行N次操作,每次将数列[a,b)这个区间中所有比k小的数改为k,他想知 道N次操作后数列中所有元素的和.他还要玩其他游戏,所以这个问题留给你解决. Input 第一行一个整数N,然后有N行,每行三个正整数a.b.k. N<=40000 , a.b.k<=10^9 Output 一个数,数列中所有元素的和 Sample Input 4 2 5

bzoj 3657 斐波那契数列(fib.cpp/pas/c/in/out)

空间 512M  时限2s [题目描述] 有n个大于1的正整数a1,a2,…,an,我们知道斐波那契数列的递推式是f(i)=f(i-1)+f(i-2),现在我们修改这个递推式变为f(i)=f(i-1)+f(i-2)+r(i-1),其中r(x)为a1,a2,…,an中为x的约数的个数.现在要求f(m) mod 19940417的值.注:初值f(1)=1,f(2)=1 输入格式: 第一行两个数n,m. 接下来一行n个正整数a1,a2,…,an. 输出格式: 输出一行仅一个数,f(m) mod 199