hdu2197 本原串(容斥)

P r o b l e m D e s c r i p t i o n Problem Description
0 1 n n < = 100000000 ) 由0和1组成的串中,不能表示为由几个相同的较小的串连接成的串,称为本原串,有多少个长为n(n<=100000000)的本原串?
m o d   2008. 答案mod\ 2008.
100100 100 1101 例如,100100不是本原串,因为他是由两个100组成,而1101是本原串。

Input
输入包括多个数据,每个数据一行,包括一个整数n,代表串的长度。

Output
对于每个测试数据,输出一行,代表有多少个符合要求本原串,答案mod2008.

Sample Input
1
2
3
4

Sample Output
2
2
6
12

题意说的是有一个长度为n的01串。每个位置都可以为0或者为1。问这个长度为n的串可以由0和1组成没有循环节的串 有多少种方案。
思路:
每个位置都可以为0或者为1,所以一共有 2 n 2^n 种方案组成这个串,由于这个串不能有循环节,我们可以反向考虑有循环节的时候。有循环节的话,假设循环节长度为 k k ,则必然有 k n k|n ,即找到n的因子,每个因子代表一个循环节的长度,比如当 n = 8 n=8 的时候,它的因子为 1 , 2 , 4 1,2,4 ,那么长度为1的串可能有 2 1 2^{1} 种,长度为2的串可能有 2 2 2^{2} 种,长度为4的串可能有 2 4 2^4 种。
依据加法定理,把这些相加就包含了所有情况,但是!!!这里面显然有重复的啊!比如长度为1的串为1,长度为2 的串为11,显然11111111这个串被重复构造,显然长度为2的循环节不能再分成长度为1 的循环节才符合我们想要的要求,即不能有11这样的长度为2的循环节。那可能么办?
我忍不住要问一句,您真的明白题目中本原串的定义吗?
本原串的定义是长度为 n n 的串没有循环节,而我们要的上述长度为k的循环节也是不能有循环节的,不然就会有重复。所以长度为 k k 的循环节显然就是长度为 k k 的本源串。
综上,我们可以递归的去寻找原串,在过程中避免重复寻找,把求过的本源串记下来就行了。
A C   C o d e : AC \ Code:

#include<iostream>
#include<cstring>
#include<queue>
#include<map>
#include<cmath>
#include<set>
#include<stack>
#include<cstdio>
#include<sstream>
#include<vector>
#include<bitset>
#include<algorithm>
#include<unordered_map>
using namespace std;
#define read(x) scanf("%d",&x)
#define Read(x,y) scanf("%d%d",&x,&y)
#define gc(x)  scanf(" %c",&x);
#define mmt(x,y)  memset(x,y,sizeof x)
#define write(x) printf("%d\n",x)
#define pii pair<int,int>
#define INF 0x3f3f3f3f
#define ll long long
const int N = 100000 + 100;
const int M = 3e6 + 1005;
typedef long long LL;
unordered_map<int,ll> hap;//记录求过的本源串
ll Qpow(ll a,ll b,ll p){
    ll ans = 1;
    a %= p;
    while(b){
        if(b&1) ans = ans * a%p;
        b >>= 1;
        a = a*a%p;
    }
    return ans;
}
ll solve(ll n){
    ll  sum = 0;
    for(int i = 1;i <= sqrt(n);++i){
        if(n % i == 0){
            if(i == 1) sum = (sum + 2)%2008;
            else {
                if(hap.count(i)) sum = (sum + hap[i]) % 2008;
                else {
                    hap[i] = solve(i);
                    sum = (sum + hap[i]) % 2008;
                }
                if(i == n/i) continue;
                if(hap.count(n/i)) sum = (sum + hap[n/i])% 2008;
                else{
                    hap[n/i] = solve(n/i);
                    sum = (sum + hap[n/i]) % 2008;
                }
            }
        }
    }
    return (Qpow(2,n,2008) - sum + 2008)%2008;
}
int main(){
    int n;
    hap[1] = 2;
    hap[2] = 2;
    hap[3] = 6;
    while(scanf("%d",&n) == 1){
        if(hap.count(n)) printf("%lld\n",hap[n]);
        else {
            hap[n] = solve(n) ;
            printf("%lld\n",hap[n]);
        }
        
    }
}
发布了632 篇原创文章 · 获赞 27 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_43408238/article/details/103618015