题目大意
给出n,m.问有多少个长度为n的序列a满足:1<=a[i]<=m,并且序列a经过若干次循环左移能变成回文.
i.e:1,1,2,2 左移一次-> 1,2,2,1.
n,m<=1e9
题解
先考虑如果没有旋转的限制的情况
显然就是
(n/2)m
(
n
/
2
)
m
,旋转的话理论上是乘n,但是显然会算重
算重的原因就是因为几个回文串可以通过题目中的方式彼此到达,那么我们只需要计算出每一个回文串到下一个回文串要多少步就好了
手玩一下,容易发现步数与这个字符串的循环节有关,如果一个字符串的长度是偶数,那么步数就是长度/2,否则就是循环节长度
从理性分析:
对于一个循环节长度为奇数的字符串,最中间的循环节的中点就是整个回文串的对称中心,移动一个循环节相当于让对称中心变成下一个循环节的中点
对于一个循环节长度为偶数的字符串,对称中心并不是落在循环节的中点上的,所以可能成为对称中心的有一个循环节的中点以及两个循环节中间的那个位置,所以是循环节/2
现在我们的问题就变成了求出每一个长度的循环节有多少个对应的字符串,使得这个循环节是对应所有字符串的最小循环节
设f[x]表示最小循环节为x的字符串的方案数,那么
f[x]=((x+1)/2)m
f
[
x
]
=
(
(
x
+
1
)
/
2
)
m
然后再减去循环节更小的
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define ll long long
using namespace std;
const int maxn=1e5+5,md=1e9+7;
ll a[maxn],f[maxn];
ll i,j,k,l,m,n,x,y,ans,now,p;
ll quickmi(ll x,ll y){
ll t1=1;
while (y){
if ((y & 1)) t1=(t1*x)%md;
x=(x*x)%md;
y/=2;
}
return t1;
}
int main(){
while (cin>>n>>k){
p=n; now=0;
for(i=1;i*i<=p;i++){
if (p%i==0){
a[++now]=i;
if ((p/i)!=i) a[++now]=p/i;
}
}
sort(a+1,a+now+1);
memset(f,0,sizeof(f)); ans=0;
fo(i,1,now){
f[i]=quickmi(k,(a[i]+1)/2);
fo(j,1,i-1) if (a[i]%a[j]==0) f[i]=(f[i]-f[j]+md)%md;
if (a[i]%2==0) ans=(ans+f[i]*(a[i]/2))%md;
else ans=(ans+f[i]*a[i])%md;
}
printf("%d\n",ans);
}
return 0;
}