SDOI2013 项链
首先我们可以求出来满足条件的珠子的总数 。有以下结论:
- 对于一个大小为 的环,用 种颜色给它染色,要求环上相邻两个点的颜色不同,那么方案数为:。
我是硬推出来这个式子的2333
具体来说,设 表示大小为 的环的答案,考虑容斥:
- 先不管第 个元素和第 个元素之间的限制,那么可以发现答案就是 。这是因为第 个元素有 种选择,后面的元素都只有 种选择。
- 再去掉第一个元素和第 个元素相同时的答案,此时可以把这两个元素看成一个,那么方案数就是 。
- 但需要特判 的情况:。
我们有 ,其中 。展开得到
把后面个求和里面的 提出来得到
由等比数列求和公式有
这就证明了结论。
考虑怎么算出 。再用一次 引理,我们设
这些都可以 求解。那么 。
本题中置换群由 种旋转构成。
如果转了 格,那么会形成 个置换环,答案就是 。显然可以 求出来。
看上去 非常不可过,但是你发现这个算法的复杂度实际上是 ,我们都知道 ,于是这个算法也就可以通过本题了。
然后我们发现 引理算完结束之后需要除以 ,但这个题 可能是 的倍数导致逆元不存在
我们可以在 意义下进行计算,这样一来如果我们算出来那个 ,那么如果 我们就计算 就可以了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read(){
int x=0,f=1;char c=getchar();
for(;(!(c>='0'&&c<='9'));c=getchar())if(c=='-')f=-1;
for(;(c>='0'&&c<='9');c=getchar())x=x*10+c-'0';
return x*f;
}
const int N=1e7;
int pri[N+5],cnt=0,mu[N+5];
bool vis[N+5];
void init(){
for(int i=2;i<=N;i++){
if(!vis[i])pri[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&i*pri[j]<=N;j++){
vis[i*pri[j]]=1;
if(i%pri[j]==0){mu[i*pri[j]]=0;break;}
mu[i*pri[j]]=-mu[i];
}
}
mu[1]=1;for(int i=1;i<=N;i++)mu[i]+=mu[i-1];
}
const int mod=1e9+7;
int mul(int x,int y,int p){
__int128 xx=x,yy=y,pp=p;
return (int)(xx*yy%pp);
}
const int MOD=mod*mod;
int ksm(int x,int y,int p=MOD){
int ans=1;
for(int i=y;i;i>>=1,x=mul(x,x,p))if(i&1)ans=mul(ans,x,p);
return ans;
}
int inv(int x,int p=MOD){
return ksm(x,p-2,p)%p;
}
const int iv=833333345000000041;
int calc(int a){
int res=0;
for(int l=1,r=0;l<=a;l=r+1){
r=a/(a/l);int w=a/l;
// cout<<l<<" "<<r<<endl;
res=(res+mul((mul(w,mul(w,w,MOD),MOD)+mul(3,mul(w,w,MOD),MOD)),(mu[r]-mu[l-1]+MOD)%MOD,MOD))%MOD;
// cout<<res<<endl;
}
res+=2;
// cout<<"res = "<<res<<endl;
return mul(res,iv,MOD);
}
int p[30],c[30],r[30],k,n,m,ans=0;
void dfs(int now){
if(now==k+1){
int phi=1,d=1;
for(int i=1;i<=k;i++){
if(r[i]>0)phi=phi*ksm(p[i],r[i]-1)*(p[i]-1);
d=d*ksm(p[i],c[i]-r[i]);
}
// cout<<d<<" | "<<phi<<endl;
ans=(ans+(mul((ksm(m-1,d)%MOD+mul(d%2==0?1:-1,m-1,MOD)+MOD)%MOD,phi%MOD,MOD)))%MOD;
return ;
}
for(int i=0;i<=c[now];i++)r[now]=i,dfs(now+1),r[now]=0;
}
void solve(){
k=0;ans=0;
n=read();int a=read();
m=calc(a);int div=n;
// cout<<m<<endl;
for(int i=2;i*i<=n;i++){
if(n%i==0){
p[++k]=i,c[k]=0;
while(n%i==0)n/=i,c[k]++;
}
}
if(n>1)p[++k]=n,c[k]=1;
// for(int i=1;i<=k;i++)cout<<p[i]<<" ^ "<<c[i]<<endl;
dfs(1);
if(div%mod!=0)cout<<ans%mod*inv(div%mod,mod)%mod<<endl;
else cout<<(ans/mod)*inv(div/mod,mod)%mod<<endl;
}
signed main(void){
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
#endif
init();
int tt=read();
while(tt--)solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】