传送门
解析:
zxyoi完成oi生涯中的第一次出题。
思路:
首先肯定是要推结论的。
令询问为
n
n
n,
m
m
m时的答案为
N
(
n
,
m
)
N(n,m)
N(n,m)
首先断环成链,同一条项链考虑
m
m
m种断开的方式,那么得到的链的数量就有
m
N
(
n
,
m
)
mN(n,m)
mN(n,m)。每个位置任意填得到的答案为
n
m
n^m
nm,其中必然有重复。
考虑序列
a
1
a
2
.
.
.
a
m
a_1a_2...a_m
a1a2...am会出现多少次,显然就是产生的循环位移
a
k
.
.
a
m
a
1
.
.
.
a
k
−
1
a_k..a_ma_1...a_{k-1}
ak..ama1...ak−1有多少与原串相同。
令
S
n
S_n
Sn表示颜色集合,则有
m
N
(
n
,
m
)
=
∑
k
=
1
m
∑
a
1
,
a
2
,
a
3
.
.
.
a
m
∈
S
n
[
a
1
a
2
.
.
.
a
m
=
a
k
.
.
.
a
m
a
1
.
.
.
a
k
−
1
]
mN(n,m)=\sum_{k=1}^{m}\sum_{a_1,a_2,a_3...a_m\in S_n}[a_1a_2...a_m=a_k...a_ma_1...a_{k-1}]
mN(n,m)=k=1∑ma1,a2,a3...am∈Sn∑[a1a2...am=ak...ama1...ak−1]
然后就很妙了。考虑当 m = 6 m=6 m=6, k = 4 k=4 k=4的时候,如何计算 a 1 a 2 a 3 a 4 a 5 a 6 = a 4 a 5 a 6 a 1 a 2 a 3 a_1a_2a_3a_4a_5a_6=a_4a_5a_6a_1a_2a_3 a1a2a3a4a5a6=a4a5a6a1a2a3有多少组解?
显然 a 1 = a 4 , a 2 = a 5 , a 3 = a 6 a_1=a_4,a_2=a_5,a_3=a_6 a1=a4,a2=a5,a3=a6,所以实际上能够随意填的只有 3 = g c d ( m , k − 1 ) 3=gcd(m,k-1) 3=gcd(m,k−1)个位置,解的数量为 n g c d ( m , k − 1 ) n^{gcd(m,k-1)} ngcd(m,k−1)。
严格的叙述为,考虑 ( m , k ) (m,k) (m,k)的解的限制。无非就是 a j = a ( j + k − 1 ) % m + 1 a_j=a_{(j+k-1)\%m+1} aj=a(j+k−1)%m+1
令 d = g c d ( m , k − 1 ) d=gcd(m,k-1) d=gcd(m,k−1),则所有的 a ( t + s × d ) % m + 1 a_{(t+s\times d)\%m+1} a(t+s×d)%m+1都是被锁死在了一起,能够随意选择的只有 a 1 , a 2 . . . a d a_1,a_2...a_d a1,a2...ad一共 d d d个数,则解的数量为 n g c d ( m , k − 1 ) n^{gcd(m,k-1)} ngcd(m,k−1)。
以上,我们证明了 m N ( n , m ) = ∑ k = 1 m n g c d ( m , k − 1 ) = ∑ k = 0 m − 1 n g c d ( m , k ) mN(n,m)=\sum_{k=1}^{m}n^{gcd(m,k-1)}=\sum_{k=0}^{m-1}n^{gcd(m,k)} mN(n,m)=k=1∑mngcd(m,k−1)=k=0∑m−1ngcd(m,k)
但是这个式子还需要化简。实测没化简过不了。
N ( n , m ) = 1 m ∑ d ∣ m n d ∑ k = 0 m − 1 [ g c d ( k , m ) = d ] N(n,m)=\frac{1}{m}\sum_{d|m}n^d\sum_{k=0}^{m-1}{[gcd(k,m)=d]} N(n,m)=m1d∣m∑ndk=0∑m−1[gcd(k,m)=d] = 1 m ∑ d ∣ m n d ∑ k = 0 m − 1 [ g c d ( k / d , m / d ) = 1 ] =\frac{1}{m}\sum_{d|m}n^d\sum_{k=0}^{m-1}[gcd(k/d,m/d)=1] =m1d∣m∑ndk=0∑m−1[gcd(k/d,m/d)=1] = 1 m ∑ d ∣ m n d ∑ k = 0 m / d − 1 [ g c d ( k , m / d ) = 1 ] =\frac{1}{m}\sum_{d|m}n^d\sum_{k=0}^{m/d-1}[gcd(k,m/d)=1] =m1d∣m∑ndk=0∑m/d−1[gcd(k,m/d)=1]
所以最终得到的式子为 N ( n , m ) = 1 m ∑ d ∣ m n d φ ( m / d ) N(n,m)=\frac{1}{m}\sum_{d|m}n^d\varphi(m/d) N(n,m)=m1d∣m∑ndφ(m/d)。
线性筛同时处理欧拉函数和因数表就行了。
这里因数表的处理有很多方法,我没有刻意去卡其他做法,询问数可以再大一点,可能就需要用这种方法了。
我这里用的是类似链表的方式,可以在 O ( ∑ σ 0 ( i ) ) O(\sum\sigma_0(i)) O(∑σ0(i))的复杂度内处理出所有因数表。其中 σ 0 ( i ) \sigma_0(i) σ0(i)表示 i i i的因数个数。
质数显然直接存就行了。
一个合数 B B B必然由质数 P P P和另一个数 A A A相乘得到,那么 A A A的所有因子必然都是 B B B的因子,那么 B B B的表末端直接连在 A A A的表末端就好了。然后考虑每个 A A A的因子 δ \delta δ,首先 δ × P \delta\times P δ×P必然是 B B B的因子,但是也有可能是 A A A的因子,这个直接判断一下再插入 B B B的链表末端就行了。
这样大部分因数表是由相当一部分数共享的,可以优化空间。
U P D UPD UPD:
抱歉,这是一道Polya的裸题
代码(已删去所有常数优化,可过):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define cs const
cs int P=500005;
cs int mod=1000000007;
int prime[P],pcnt;
bool mark[P];
vector<int> factor;
vector<int> pre;
int last[P],phi[P],inv[P];
void _copy(int goal,int f,int p){
bool flag=false;
for(int i=last[f];~i;i=pre[i]){
int v=factor[i];
if(f%(v*p)){
if(flag)
pre.push_back(factor.size()-1);
else pre.push_back(last[f]),flag=true;
factor.push_back(v*p);
}
}
last[goal]=pre.size()-1;
}
void linear_sieves(int len=P-5){
last[1]=0;phi[1]=1;
factor.push_back(1);
pre.push_back(-1);
for(int i=2;i<=len;++i){
if(!mark[i]){
prime[++pcnt]=i;
phi[i]=i-1;
_copy(i,1,i);
}
for(int j=1;j<=pcnt&&i*prime[j]<=len;++j){
mark[i*prime[j]]=true;
if(i%prime[j]==0){
phi[i*prime[j]]=phi[i]*prime[j];
_copy(i*prime[j],i,prime[j]);
break;
}
phi[i*prime[j]]=phi[i]*(prime[j]-1);
_copy(i*prime[j],i,prime[j]);
}
}
}
int quickpow(int a,int b){
int ans=1;
while(b){
if(b&1)ans=(ll)ans*a%mod;
b>>=1;
a=(ll)a*a%mod;
}
return ans;
}
int query(int n,int m){
int ans=0;
for(int i=last[m];~i;i=pre[i]){
ans=((ll)ans+(ll)quickpow(n,factor[i])*phi[m/factor[i]])%mod;
}
return (ll)ans*inv[m]%mod;
}
int T;
signed main(){
linear_sieves();
inv[1]=inv[0]=1;
for(int i=2;i<=P-5;++i){
inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
}
scanf("%d",&T);
while(T--){
int n,m;
scanf("%d%d",&n,&m);
printf("%d\n",query(n,m));
}
return 0;
}