【ARC064-F】【XSY2575】Rotated Palindromes(DP)(字符串)

Description
然而,由于小C沉迷于制作游戏,他完全忘记了自己作为国家集训队的一员,还有156道作业题等他完成。还有一天作业就要截止了,而他一题还没有做。于是他赶紧挑了一道看起来最简单的题:

“给定一个整数N,请你求出有多少字符集为1到K之间整数的字符串,使得该字符串可以由一个长度为N的回文串循环移位后得到。所谓循环移位,就是把字符串的某个前缀(可以为空)移到字符串末尾,如"1221"循环移位可以得到"1221"、"2211"、"2112"、"1122"四个字符串。结果对109+7取模。”

为了不让小C的集训队资格被CCF取消,请你帮助他完成这道题吧。

Input

第一行包含两个整数N,K。

Output

输出满足条件的字符串数对109+7取模的结果。

Sample Input

Sample Input 1

4 2

Sample Input 2

1 10

Sample Input 3

6 3

Sample Input 4

1000000000 1000000000

Sample Output

Sample Output 1

6

Sample Output 2

10

Sample Output 3

75

Sample Output 4

875699961

HINT

在第一个样例中,有"1111"、"1122"、"1221"、"2211"、"2112"、"2222",共6个字符串符合条件。

我们先考虑枚举回文串,因为回文串是回文的,所以我们只用枚举回文串的一半,回文串的个数就是\(m^{(n+1)/2}\)

每一个回文串应该有\(n\)种循环移位后的到的字符串,但是这样计算后我们会发现这比答案大很多,因为有重复。

我们考虑如何计算不重复的字符串的个数,我们可以枚举回文串的循环节。

分两种情况讨论。

1.循环节为奇数

因为一个回文串的循环节必定为回文串,所以我们循环移位1个循环节的长度为\(len\)就会有重复,所以会产生\(len\)个新字符串。

2.循环节为偶数

因为上述的原因,循环节必定回文,我们只要有\(len/2\)的长度就会重复。

比如说,串\(122111221\),我们移了两位之后,串变成\(21122112\),虽然看起来与原串并不相同,但是我们在枚举回文串的时候,也会枚举到\(21122112\),所以也算重复。

接下来DP。

我们先预处理出\(n\)的所有因数,\(a[]\)

我们设\(dp[i]\)表示最小循环节为\(a[i]\)时,有多少个符合条件的回文串。

我们在\(dp[i]\)中减去\(dp[a[i]的因子]\),为了保证\(a[i]\)时最小的循环节。

ans就直接加上回文串所贡献的字符串个数即可。


#include<bits/stdc++.h>
#define mod 1000000007
#define int long long
using namespace std;
int dp[2010],cnt,n,k,a[2010],ans;
int fastpow(int x,int y)
{
    int sum=1;
    while(y)
    {
        if(y&1)
        {
            sum=(1ll*sum*x)%mod;
        }
        x=(1ll*x*x)%mod;
        y>>=1;
    }
    return sum;
}
signed main()
{
    scanf("%lld%lld",&n,&k);
    for(int i=1;i*i<=n;i++)//枚举因子
    {
        if(n%i==0)
        {
            a[++cnt]=i;
            if(i*i!=n)
            {
                a[++cnt]=n/i;
            }
        }
    }
    sort(a+1,a+cnt+1);
    for(int i=1;i<=cnt;i++)
    {
        dp[i]=fastpow(k,(a[i]+1)/2);//有多少个回文串
        for(int j=1;j<i;j++)
        {
            if(a[i]%a[j]==0)//去重
            {
                dp[i]=(dp[i]-dp[j]+mod)%mod;
            }
        }
        if(a[i]&1)//统计答案
        {
            ans=(ans+dp[i]*a[i])%mod;
        }else{
            ans=(ans+dp[i]*(a[i]/2))%mod;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
posted @ 2019-09-14 20:53  ezoi_ly  阅读(169)  评论(0编辑  收藏  举报