[HDU2481]Toy(置換+矩陣乘法)

=== ===

這裏放傳送門

=== ===

題解

一眼看到這個題的時候想到了BZOJ1002輪狀病毒。。。實際上做這個題的時候也用到了那個題的結論。

結論:設f(n) 爲不同的(不考慮旋轉同構)的n輪狀病毒的個數,則f(n)=3f(n1)f(n2)+2

這玩意兒當時是用基爾霍夫矩陣推出來的。。
但是這個題的話要考慮旋轉同構誒,如何和那個題的結論聯繫起來呢?

Burnside引理:設有置換羣G={a1,a2...ag}c(ai) 爲置換ai 作用下不動點的個數,那麼這個置換羣作用下等價類的個數爲1Gi=1gc(ai)

那麼關鍵就是如何求出在每種置換作用下不動點的個數,也就是經過這個置換作用以後不變的方案數目。設旋轉i 次的置換爲ai ,那麼顯然置換總數爲nai 的循環節個數爲gcd(i,n)
可以發現如果這個圖被分成了i 個循環節,那麼屬於第i 個循環節的點一定和屬於第i%n+1 和第i%n1 個循環節(啊這裏什麼減出負數來的細節就不管了反正就是要表達一個循環中的前一個點和後一個點的意思啦)的點是挨着的。

那麼如果要構造在置換ai 作用下的同構方案的話,顯然如果某個屬於循環節x 的點和某個屬於循環節y 之間的點有邊相連,那麼所有屬於循環節x 的點都必須和對應位置循環節y 的點相連,不然旋轉一下的話看起來就不一樣了。
這說明這個連邊方案只和循環節的個數有關,屬於同一個循環節的點可以看成一個整體,而如果把循環節看成整體的話就相當於可以隨便連邊不用考慮什麼同構限制了。那這就跟輪狀病毒那個題是一樣了。
設n輪狀病毒的方案數爲f(n) ,那麼我們要求的結果就是i=1nf(gcd(i,n))
用數學方法化一下式子就可以化成d|nφ(d)f(nd) ,用O(n) 的時間就可以求解。

注意的事情是這裏的模數不一定和n互質,在做除法的時候沒法求逆元,那隻能先把m 乘上n ,最後除以n 再模m ,而這就造成了模數非常大,直接乘可能炸long long,所以要用快速乘。

代碼

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,prm[1000010];
long long ans,m,f[5],Mod,phi[1000010];
bool ext[1000010];
inline long long mul(long long a,long long t){
    long long ans=0;
    if (a<0) a+=Mod;
    if (t<0) t+=Mod;
    while (t!=0){
        if (t&1) ans=ans+a;
        if (ans>=Mod) ans-=Mod;
        a=a+a;t>>=1;
        if (a>=Mod) a-=Mod;
    }
    return ans;
}
struct hxy{
    long long s[5][5];
    hxy(){memset(s,0,sizeof(s));}
    void clear(){
        memset(s,0,sizeof(s));
        for (int i=1;i<=3;i++) s[i][i]=1;
    }
    void get(){
        memset(s,0,sizeof(s));
        s[1][1]=1;s[3][2]=1;s[1][3]=2;
        s[2][3]=-1;s[3][3]=3;
    }
    hxy operator * (const hxy &a){
        hxy c;
        long long *w;
        for (int i=1;i<=3;i++)
          for (int j=1;j<=3;j++){
              w=&(c.s[i][j]);
              (*w)=((*w)+mul(s[i][1],a.s[1][j]))%Mod;
              (*w)=((*w)+mul(s[i][2],a.s[2][j]))%Mod;
              (*w)=((*w)+mul(s[i][3],a.s[3][j]))%Mod;
          }
        return c;
    }
}w;
hxy powww(hxy a,int t){
    hxy ans;
    ans.clear();
    while (t!=0){
        if (t&1) ans=ans*a;
        a=a*a;t>>=1;
    }
    return ans;
}
void get_prime(int N){
    phi[1]=1;
    for (int i=2;i<=N;i++){
        if (ext[i]==false){
            prm[++prm[0]]=i;
            phi[i]=i-1;
        }
        for (int j=1;j<=prm[0];j++){
            if ((long long)i*prm[j]>N) break;
            ext[i*prm[j]]=true;
            if (i%prm[j]==0){
                phi[i*prm[j]]=phi[i]*prm[j];break;
            }else phi[i*prm[j]]=phi[i]*phi[prm[j]];
        }
    }
}
long long PHI(int N){
    if (N<=1000000) return phi[N]%Mod;
    long long ans=N;
    for (int i=1;i<=prm[0];i++){
        int v=prm[i];
        if ((long long)v*v>N) break;
        if (N%v==0) ans=ans-ans/v;
        while (N%v==0) N/=v;
        if (N==1) break;
    }
    if (N!=1) ans=ans-ans/N;
    return ans%Mod;
}
long long F(int N){
    long long h[5];
    if (N<=2) return f[N+1];
    memset(h,0,sizeof(h));
    w.get();
    w=powww(w,N-2);
    for (int i=1;i<=3;i++)
      for (int j=1;j<=3;j++)
        h[i]=(h[i]+mul(f[j],w.s[j][i]))%Mod;
    return h[3];
}
int main()
{
    get_prime(1000000);
    while (scanf("%d%d",&n,&m)!=EOF){
        ans=0;Mod=(long long)m*n;
        f[1]=1;f[2]=1;f[3]=5;
        for (int i=1;i*i<=n;i++)
          if (n%i==0)
            if (i*i==n) ans=(ans+mul(PHI(i),F(i)))%Mod;
            else{
                ans=(ans+mul(PHI(i),F(n/i)))%Mod;
                ans=(ans+mul(PHI(n/i),F(i)))%Mod;
            }
        ans=(ans+Mod)%Mod;
        ans=(ans/n)%m;
        printf("%I64d\n",ans);
    }
    return 0;
}
發佈了163 篇原創文章 · 獲贊 68 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章