在一个圆中, 一共 个角度, 初始有个物品, 分别在个不同的角度, 个颜色去染这 个物品, 求方案数量.
旋转同构 .
这道题目为项链问题的一个拓展,
相比项链问题, 该题的 项链 旋转后不一定与原来重合,
对该问题目标是:
旋转后使得每个角度重合, 等价于每个角度的 间距 相同, 于是将位置数组转换为间距数组,
进行 环状处理.
- 将所有物品的角度排序, 进行差分操作
- 数组拷贝一次放在后方
- 原始差分数组作为模式串, 倍长数组作为文本串, 进行 字符串匹配
- 若匹配成功, 说明旋转若干角度可以与原来重合, 计入 置换总数 ,
其中为旋转多少度 . - 除, 输出答案.
#include<cstdio>
#include<cctype>
#include<algorithm>
#define reg register
int read(){
char c;
int s = 0, flag = 1;
while((c=getchar()) && !isdigit(c))
if(c == '-'){ flag = -1, c = getchar(); break ; }
while(isdigit(c)) s = s*10 + c-'0', c = getchar();
return s * flag;
}
const int maxn = 360005;
const int mod = 100000007;
int S;
int P;
int Ans;
int A[maxn<<1];
int B[maxn];
int nxt[maxn];
int Gcd(int a, int b){ return !b?a:Gcd(b, a%b); }
int KSM(int a, int b){
int s = 1;
while(b){
if(b & 1) s = 1ll*s*a % mod;
a = 1ll*a*a % mod;
b >>= 1;
}
return s;
}
void Get_next(){
nxt[0] = -1;
int i = 0, t = -1;
while(i < P-1){
if(t == -1 || B[i] == B[t]) nxt[++ i] = ++ t;
else t = nxt[t];
}
}
void KMP(){
int cnt = 0;
int t_1 = 0, t_2 = 2;
while(t_2 < (P<<1)){
if(t_1 == -1 || B[t_1] == A[t_2]) t_1 ++, t_2 ++;
else t_1 = nxt[t_1];
if(t_1 == P-1){
int k = t_2 - P - 1;
Ans = ( 1ll*Ans + KSM(S, Gcd(P, k) ) ) % mod;
t_1 = nxt[t_1];
cnt ++;
}
}
Ans = 1ll*Ans*KSM(cnt, mod-2) % mod;
}
void Work(){
for(reg int i = 1; i <= P; i ++) A[i] = read();
if(P == 1){ printf("%d
", S); return ; }
std::sort(A+1, A+P+1);
for(reg int i = 1; i <= P; i ++) A[i+P] = A[i] + 360000;
for(reg int i = P<<1; i > 1; i --) A[i] -= A[i-1];
for(reg int i = 1; i <= P; i ++) B[i-1] = A[i+1];
Ans = 0;
Get_next();
KMP();
printf("%d
", Ans);
}
int main(){
S = read(), P = read();
while(~S && ~P) Work(), S = read(), P = read();
return 0;
}