HDU 2197 本原串

Posted suvvm

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU 2197 本原串相关的知识,希望对你有一定的参考价值。

  本原串

  由0和1组成的串中,不能表示为由几个相同的较小的串连接成的串,称为本原串,有多少个长为n(n<=100000000)的本原串? 
答案mod2008. 
  例如,100100不是本原串,因为他是由两个100组成,而1101是本原串。 
Input
  输入包括多个数据,每个数据一行,包括一个整数n,代表串的长度。
Output
  对于每个测试数据,输出一行,代表有多少个符合要求本原串,答案mod2008. 
Sample Input
1
2
3
4
Sample Output
2
2
6
12

解题思路:
  本题是一个数学问题,串的每一位不是0就是1,给出一个数字n为串的长度,要求计算这个长度的本原串数量。

  由于串的每一位都是0或1,所以n长度下最多有n^2个不同的串。我们用ans[n]表示当前长度下的本原串数量,若想获得本原串数量,可以用当前串的总数减去不是本原串的数量。当n不等于1时,全由1或全由0组成的串肯定不是本源串。所以当前ans[n] = 2^n - 2,回想题中对本原串的定义,非本源串是由某一个长度的本原串重复数次得到的,我们设n的因子为m,则m长度的本原串重复n / m 次一定可以得到n长度的非本原串,且n/  m也为n的因子,长度为n / m的本原串重复m次也一定可以得到长度为n的非本原串,那么我们只需找到当前串长度的所有因子长度的本原串数量即可找到其余所有非本原串数量。

  由于n较大所以使用快速幂

  快速幂思想:求2^11,11的二进制位1011,11 = 1*2^3  +  0*2^2  +  1*2^1  +  1*2^0,所以可以将2^11转化为2^(2^0) * 2^(2^1) * 2(2^3)。将原先的11次 O(n)优化为了3次O(logn),本题要求取模,又因为积的取余等于取余的积的取余,我们可以直接在快速幂的过程中取模以得到取模后的答案。

  快速幂取模:

  

技术分享图片
LL power(int a, int b, int mod){    //快速幂
    LL ans = 1;
    while(b){
        if(b & 1){  //从b的二进制末位开始判断
            ans = ans * a % mod;  //如果为1更新取模后的答案
        }
        a = a * a % mod;    //更新底数取模
        b >>= 1;    //b右移一位
    }
    return ans;
}
快速幂取模

 

  AC代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 typedef long long LL;
 4 const int maxn = 100000005;
 5 int ans[maxn];
 6 LL power(int a, int b, int mod){    //快速幂
 7     LL ans = 1;
 8     while(b){
 9         if(b & 1){  //从b的二进制末位开始判断
10             ans = ans * a % mod;  //如果为1更新取模后的答案
11         }
12         a = a * a % mod;    //更新底数取模
13         b >>= 1;    //b右移一位
14     }
15     return ans;
16 }
17 LL clt(int n){  //传入当前长度
18     if(ans[n] != 0) //如果ans[n]不为0证明之前的计算已经计算完成当前长度的本原串数量直接返回答案
19         return ans[n];
20     ans[n] = power(2, n, 2008) - 2; //减去全0与全1
21     for(int i = 2; i * i <= n; i++){    //寻找因子
22         if(n % i == 0){ //i为因子
23             ans[n] = (ans[n] - clt(i) + 2008) % 2008;
24             //当前数量减去因子长度的本原串数量,由于做减运算可能出现负数所以加上取模数再取模
25             if(i * i != n)
26                 ans[n] = (ans[n] - clt(n / i) + 2008) % 2008;
27                 //随便计算另一个因子
28         }
29     }
30     return ans[n];
31 }
32 int main()
33 {
34     ans[0] = 0, ans[1] = 2, ans[2] = 2; //初始化长度为0 1 2时的本原串数量
35     int n;
36     //clt(100000005);本题不能打表,会超时
37     while(scanf("%d", &n) != EOF){
38         if(n > 2)   //n大于2进行运算
39             ans[n] = clt(n);
40         printf("%d
", ans[n]);
41     }
42     return 0;
43 }

 





以上是关于HDU 2197 本原串的主要内容,如果未能解决你的问题,请参考以下文章

数论专题hdu2197

loj 3311「ZJOI2020」字符串 - 平方串

HDU3247 Resource Archiver(AC自动机+BFS+DP)

hdu 5950 Recursive sequence

求25的所有本原根Python实现

HDU 2203亲和串