传送门
解析:
zxyoi完成oi生涯中的第一次出题。
思路:
首先肯定是要推结论的。
令询问为nnn,mmm时的答案为N(n,m)N(n,m)N(n,m)
首先断环成链,同一条项链考虑mmm种断开的方式,那么得到的链的数量就有mN(n,m)mN(n,m)mN(n,m)。每个位置任意填得到的答案为nmn^mnm,其中必然有重复。
考虑序列a1a2...ama_1a_2...a_ma1a2...am会出现多少次,显然就是产生的循环位移ak..ama1...ak−1a_k..a_ma_1...a_{k-1}ak..ama1...ak−1有多少与原串相同。
令SnS_nSn表示颜色集合,则有mN(n,m)=∑k=1m∑a1,a2,a3...am∈Sn[a1a2...am=ak...ama1...ak−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=6m=6m=6,k=4k=4k=4的时候,如何计算a1a2a3a4a5a6=a4a5a6a1a2a3a_1a_2a_3a_4a_5a_6=a_4a_5a_6a_1a_2a_3a1a2a3a4a5a6=a4a5a6a1a2a3有多少组解?
显然a1=a4,a2=a5,a3=a6a_1=a_4,a_2=a_5,a_3=a_6a1=a4,a2=a5,a3=a6,所以实际上能够随意填的只有3=gcd(m,k−1)3=gcd(m,k-1)3=gcd(m,k−1)个位置,解的数量为ngcd(m,k−1)n^{gcd(m,k-1)}ngcd(m,k−1)。
严格的叙述为,考虑(m,k)(m,k)(m,k)的解的限制。无非就是aj=a(j+k−1)%m+1a_j=a_{(j+k-1)\%m+1}aj=a(j+k−1)%m+1
令d=gcd(m,k−1)d=gcd(m,k-1)d=gcd(m,k−1),则所有的a(t+s×d)%m+1a_{(t+s\times d)\%m+1}a(t+s×d)%m+1都是被锁死在了一起,能够随意选择的只有a1,a2...ada_1,a_2...a_da1,a2...ad一共ddd个数,则解的数量为ngcd(m,k−1)n^{gcd(m,k-1)}ngcd(m,k−1)。
以上,我们证明了mN(n,m)=∑k=1mngcd(m,k−1)=∑k=0m−1ngcd(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)=1m∑d∣mnd∑k=0m−1[gcd(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]=1m∑d∣mnd∑k=0m−1[gcd(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]=1m∑d∣mnd∑k=0m/d−1[gcd(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)=1m∑d∣mndφ(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)表示iii的因数个数。
质数显然直接存就行了。
一个合数BBB必然由质数PPP和另一个数AAA相乘得到,那么AAA的所有因子必然都是BBB的因子,那么BBB的表末端直接连在AAA的表末端就好了。然后考虑每个AAA的因子δ\deltaδ,首先δ×P\delta\times Pδ×P必然是BBB的因子,但是也有可能是AAA的因子,这个直接判断一下再插入BBB的链表末端就行了。
这样大部分因数表是由相当一部分数共享的,可以优化空间。
UPDUPDUPD:
抱歉,这是一道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;
}