【AtCoder】【ARC064F】Rotated Palindromes

Description

求有多少个序列满足以下条件:
1. 序列有n位;
2. 序列的每位为1~m之间的整数;
3. 这个序列经过旋转以后可以变成一个回文串;

Solution

这是一个悲惨的故事…..想了一天多,一直在想怎么减掉不合法的,最后一怒之下瞄了一眼(真的就是瞄一眼)标程,咦标称是直接统计耶,下一瞬间:
WOC这不是大水题吗

对于每个回文串,假设它旋转了x次以后第一次变成回文的,那么它对答案就有x的贡献(转0次~转x-1次),

考虑怎样的回文串转x次会变成回文的,(先假设x为n的约数)
把n切成(n/x)段,也就是每段有x个,
如果n/x为奇数,那么,只有这(n/x)段都相同且为回文的,它转x后还是回文的,
比如:(x=3,n=9)121 121 121;
如果n/x为偶数,那么, 这一段可以为任意,保证每(2x)位为一个回文串即可,
比如:(x=2,n=8)12 21 12 21;

根据上面计算方法,可得出,一个回文串如果转x次为回文串,那么转x的约数次也为回文串,(比如:转6次变成回文的,那么转12、18次也是回文的)

我们真正要统计的是,对于每个回文串,假设它旋转了x次以后第一次变成回文的,所以要减掉重复的,
显然,如果x不为n的约数,贡献为0,

复杂度可以参考枚举子集的复杂度,即 O ( 3 x ) (大概),不会很大。

Code

#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <map>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define efo(i,q) for(int i=A[q];i;i=B[i][0])
using namespace std;
typedef long long LL;
const int N=100500,mo=1e9+7;
int m,n;
int fj[N][2],fj0;
int d[N];
LL ans;
map<int,int>f;
LL ksm(LL q,int w)
{
    LL ans=1;
    for(;w;w>>=1,q=q*q%mo)if(w&1)ans=ans*q%mo;
    return ans;
}
int ss1(int q,int w,int e)
{
    if(q>w)return f[e];
    int ans=ss1(q+1,w,e);
    fo(i,1,d[q])((ans+=ss1(q+1,w,(e=e*fj[q][0])))>=mo?ans-=mo:0);
    return ans;
}
void ss(int q,int e)
{
    if(q>fj0)
    {
        LL t=(((n/e)%2?ksm(m,(e+1)>>1):ksm(m,e))-ss1(1,q,1)+mo)%mo;
        f[e]=t;
        ans=(ans+t*e)%mo;
        return;
    }
    d[q]=0;ss(q+1,e);
    fo(i,1,fj[q][1])d[q]=i,ss(q+1,(e*=fj[q][0]));
}
int main()
{
    int q,w;
    scanf("%d%d",&n,&m);
    if(n==1)return printf("%d\n",m),0;
    q=n;
    for(int i=2;i*i<=q;++i)if(q%i==0)
    {
        for(fj[++fj0][0]=i;!(q%i);++fj[fj0][1],q/=i);
    }
    if(q>1)fj[++fj0][0]=q,fj[fj0][1]=1;
    ss(1,1);
    printf("%lld\n",(ans+mo)%mo);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/howarli/article/details/79331880