【前言】
这是一道什么都有的数论题,我想的时候一脸懵逼,根本不会做。
在使用“特殊方法”后搞了出来,但是码的过程也一波三折。
一道神题!
【题目】
BZOJ地址
HDU地址
外面有一圈
n
个结点,中心有一个结点与
【分析】
我们接下来将看到:素数筛选,求解欧拉函数,BurnSide引理,二分模拟乘法(快速乘),递推的构造,矩阵快速幂,置换群,枚举……
【解题思路】
参考博文
根据分析发现旋转似乎并不重要,我们算出所有结果后除以
n
就行了。
我们考虑任取两个节点
那么总数便是
a,b
断开的种数与
a,b
连在一起的种数的和。
f(n)
表示外圈有
n
个结点时,而
g(n)
表示外圈有
n
个结点时,而
如果
a,b
之间是断开的,此时若与
a
直接相连的为
如果
a,b
是连在一起的,此时若与
a,b
相连的为
k
个(包括
则最终的种数便是 T[n]=f[n]+g[n] 。
接下来就是式子的化简,这部分我倒是自己尝试了一下,不算很难。
令 s[n] 为 f[i] 的前 n 项和,则有:
差不多的我们继续推 g[n]
那么我们有:
这样:
因此:
其中
f[0]=1
,但
s[n]
是不包含
f[0]
的
处理到这里,我们可以用矩阵快速幂求出
f[n]
,顺便也可以将
g[n]
搞出来。
所以
T[n]
就解决咯。
关于矩阵的构造,我们令:
那么可以得到:
接下来还要上Burnside定理+欧拉函数优化。
我们要考虑的是不动点的数量,所以前
gcd(i,n)
个点的连边方式确定下来,整个图的连边方式就确定了
最后一个点的下一个点可以看成是第一个点(即最后一段的下一段可以看成是第一段),实际上这是一个环,所以这
gcd(i,n)
个点的连接方式与
T[gcd(i,n)]
是等价的,即合法性等效
所以答案是
∑ni=1T[gcd(i,n)]
化简以后就是
∑ni=1(T[i]×ϕ(ni))(i|n)
大概应该是这样的吧,可能最后一步有点不对qwq。
然后因为
n,m
不一定互质,我们也许还要用CRT。
但其实我们可以用这个式子:
证明也十分简单
设 a=bk
所以运算时模 nm ,最后除以 m 就可以了
以上,就是这道神题了。
接下来是作弊时间
暴力打出前六个数字:1,3,6,13,25,58
丢进OEIS
得到规律:1/n*Sum_{d divides n} phi(n/d)*A004146(d)
A004146的公式是这样: a(n+1) = 3*a(n) - a(n-1) + 2
以上就是一种珂学的方法了。
【参考代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=4e4+4;
const int M=1e9+10;
int n,cnt;
LL mod;
bool flag[N];
int pri[N];
struct Matrix
{
LL m[2][2];
Matrix(){
memset(m,0,sizeof(m));
}
};
Matrix init;
LL mul(LL a,LL b)
{
a%=mod;b%=mod;
if(a<0) a+=mod;
if(b<0) b+=mod;
LL ret=0;
while(b)
{
if(b&1)
{
ret+=a;
if(ret>=mod)
ret-=mod;
}
a<<=1ll;
if(a>=mod)
a-=mod;
b>>=1ll;
}
return ret;
}
Matrix operator * (Matrix a,Matrix b)
{
Matrix ret;
for(int i=0;i<2;++i)
for(int j=0;j<2;++j)
for(int k=0;k<2;++k)
(ret.m[i][j]+=mul(a.m[i][k],b.m[k][j]))%=mod;
return ret;
}
Matrix operator ^ (Matrix a,int b)
{
Matrix ret;
for(int i=0;i<2;++i)
for(int j=0;j<2;++j)
ret.m[i][j]=(i==j);
while(b)
{
if(b&1)
ret=ret*a;
a=a*a;
b>>=1;
}
return ret;
}
void Prime()
{
int t=sqrt(M);
for(int i=0;i<=t;++i)
flag[i]=false;
flag[1]=true;
for(int i=2;i<=t;++i)
{
if(!flag[i])
pri[cnt++]=i;
for(int j=0;j<cnt && i*pri[j]<=t;++j)
{
flag[i*pri[j]]=true;
if(!(i%pri[j]))
break;
}
}
}
int Eular(int t)
{
int ret=1;
for(int i=0;i<cnt && pri[i]*pri[i]<=t;++i)
{
if(!(t%pri[i]))
{
t/=pri[i];ret*=pri[i]-1;
while(!(t%pri[i]))
{
t/=pri[i];
ret=(ret*pri[i])%mod;
}
}
}
if(t>1)
ret*=t-1;
return ret%mod;
}
LL Get_T(int k)
{
if(k==1)
return 1;
else
if(k==2)
return 5;
Matrix tmp=init^(k-2);
LL f=3ll*tmp.m[0][0]+1ll*tmp.m[1][0]%mod;
LL g=2ll*(f-(3ll*tmp.m[0][1]+tmp.m[1][1])-1)%mod;
return ((g+f)%mod+mod)%mod;
}
LL Polya()
{
LL sum=0;
int i;
for(i=1;i*i<n;++i)
if(!(n%i))
{
sum=(sum+mul(Eular(i),Get_T(n/i)))%mod;
sum=(sum+mul(Eular(n/i),Get_T(i)))%mod;
}
if(i*i==n)
sum=(sum+mul(Get_T(i),Eular(i)))%mod;
return sum/n;
}
int main()
{
// freopen("BZOJ3113.in","r",stdin);
// freopen("BZOJ3113.out","w",stdout);
Prime();
init.m[0][0]=3;init.m[0][1]=1;init.m[1][0]=-1;init.m[1][1]=0;
while(scanf("%d%lld",&n,&mod)!=EOF)
{
mod=1ll*n*mod;
printf("%lld\n",Polya()%(mod/n));
}
return 0;
}
【总结】
数论真是太可怕了!我们还是OEIS吧!