Description
小C同学认为跑步非常有趣,于是决定制作一款叫做《天天爱跑步》的游戏。《天天爱跑步》是一个养成类游戏,需要玩家每天按时上线,完成打卡任务。
这个游戏的地图可以看作一棵包含 n 个结点和 n−1 条边的树,每条边连接两个结点,且任意两个结点存在一条路径互相可达。树上结点编号为从 1 到 n 的连续正整数。
现在有 m 个玩家,第 i 个玩家的起点为 Si,终点为 Ti。每天打卡任务开始时,所有玩家在第 0 秒同时从自己的起点出发,以每秒跑一条边的速度,不间断地沿着最短路径向着自己的终点跑去,跑到终点后该玩家就算完成了打卡任务。(由于地图是一棵树,所以每个人的路径是唯一的)
小C想知道游戏的活跃度,所以在每个结点上都放置了一个观察员。在结点 j 的观察员会选择在第 Wj 秒观察玩家,一个玩家能被这个观察员观察到当且仅当该玩家在第 Wj 秒也正好到达了结点 j。
然而,由于小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个字符串符合条件。
数据范围:
1≤N,K≤109
存在10分的子任务,满足1≤N,K≤10。
存在20分的子任务(独立于上一个子任务),满足1≤N,K≤2000。
思路
先考虑如果没有旋转的限制的情况
显然就是(n/2)m,旋转的话理论上是乘n,但是显然会算重
算重的原因就是因为几个回文串可以通过题目中的方式彼此到达
那么我们只需要计算出每一个回文串到下一个回文串要多少步就好了
容易发现步数与这个字符串的循环节有关,如果一个字符串的长度是偶数,那么步数就是长度/2,否则就是循环节长度
现在我们的问题就变成了求出每一个长度的循环节有多少个对应的字符串,使得这个循环节是对应所有字符串的最小循环节
设f[x]表示最小循环节为x的字符串的方案数,那么f[x]=((x+1)/2)m然后再减去循环节更小的
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
const int N=2e5+20;
vector<ll> v;
ll n,k,f[2500];
ll power(ll x,ll n)
{
ll s=1;
while(n)
{
if(n&1) s=(s*x)%mod;
n>>=1;
x=(x*x)%mod;
}
return s;
}
void init()
{
v.clear();
for(ll i=1;i*i<=n;i++)
{
if(n%i==0)
{
v.push_back(i);
if(i*i!=n) v.push_back(n/i);
}
}
sort(v.begin(),v.end());
}
int main()
{
scanf("%d%d",&n,&k);
init();
ll ans=0;
for(int x=0;x<v.size();x++)
{
f[x]=power(k,(v[x]+1)/2);
for(int y=0;y<x;y++)
{
if(v[x]%v[y]==0) f[x]=(f[x]-f[y]+mod)%mod;
}
if(v[x]%2) ans=(ans+(f[x]*v[x])%mod)%mod;
else ans=(ans+(f[x]*v[x]/2)%mod)%mod;
}
printf("%lld",ans);
}