[ARC064F] Rotated Palindromes
题意
给定一个整数N,请你求出有多少字符集为1到K之间整数的字符串,使得该字符串可以由一个长度为N的回文串循环移位后得到。所谓循环移位,就是把字符串的某个前缀(可以为空)移到字符串末尾,如"1221"循环移位可以得到"1221"、"2211"、"2112"、"1122"四个字符串。结果对$10^9+7$取模
题解
考试时花了差不多一个小时推了一个$O(n)$的式子,还妄想着能优化到$O(sqrt(n))$......
首先,考虑一个回文串对答案的贡献:
设其最小循环节长度为len。
其贡献就是len。
但是一个回文串的循环节必定也是回文串(想不明白可以看这个:abcabcabc)
所以对于len为偶数的情况,循环节可以从中间拆开,这就导致了互换前后段后相同的循环节,例如2112,1221会扩展出一样的结果,所以要此时贡献要除以2.
但对于长度为奇数的情况,由于有中间的一位,所以互换前后段后扩展出来的仍是不同的结果,所以不用除以2。
因为len相同的回文串对答案的贡献相同,所以对于每一个len,统计代表的回文串个数即可。
又因为len一定是n的因数(不然何来循环),所以直接根号n预处理出n的因数即可。
代码
#include <algorithm> #include <iostream> #include <vector> #include <cmath> using namespace std; #define int long long #define mod (int)1000000007 #define N 64000 vector<int> vec; int tot[N]; int pow(int base,int up) { int ans=1; while(up) { if(up&1) ans*=base,ans%=mod; base*=base; base%=mod; up>>=1; } return ans; } signed main() { int n,k,len; cin>>n>>k; len=sqrt(n); for(int i=1;i<=len;i++) if(n%i==0)//获取所有可能的循环节长度 { vec.push_back(i); if(i!=n/i) vec.push_back(n/i); } sort(vec.begin(), vec.end()); int ans=0; for(int i=0;i<vec.size();i++) { tot[i]=pow(k,(vec[i]+1)/2);//循环节长度为vec[i]时循环节的数量。因为是回文串,所以只需要知道前面半截即可确定出整个回文串,下面也是同理 for(int j=0;j<i;j++) if(vec[i]%vec[j]==0) tot[i]-=tot[j],tot[i]=(tot[i]+mod)%mod;//去除【长度为vec[i]的循环节】内部含有循环节的情况 if(vec[i]&1) ans+=tot[i]*vec[i],ans%=mod; else ans+=tot[i]*(vec[i]/2),ans%=mod; } cout<<ans; }
看都看了,顺手点个推荐呗 :)