【NOIP2017提高A组冲刺11.6】项链

Description

给一个n个点的环染m种颜色,有3种操作,分别为旋转、翻转和把所有颜色编号+1
如果两个方案在经过任意次操作之后等价了这两个方案就是等价的
求所有本质不同的染色方案数
n,m<=1e18,保证n,m逆元存在

Solution

burnside裸题,但是多了一个颜色操作
把颜色操作也视为置换,考虑他对经典的两种置换的影响
对于旋转,我们每一组的循环节长度为n/(i,n)
如果它在+把所有颜色d(0<=d< m)后能变回它自己它就是一个不动点
那么循环节种的每一个元素在模m意义下都比上一个元素大d
能变回原样就是n/(i,n)d≡ 0 mod m
经过一些简单的数论推导我们可以得出d有(m,n/(i,n))种取值
于是我们的旋转就变成了

i=1nm(i,n)(m,n(i,n))

同理可以发现翻转时我们只有当对称轴不经过点时会产生长度为2的循环节
于是翻转可以O(1)解决
考虑旋转的式子,枚举n的因数i
i|nmi(m,ni)j=1n[gcd(j,n)=i]

i|nmi(m,ni)φ(ni)

如果我们能对n分解质因数那么这道题就做完了
发现n可以达到10^18,于是我们需要用Pollard_Rho来分解
然后就做完了

Code

#include 
#include 
#include 
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

typedef long long ll;

const int Mo=998244353,S=3;

ll mult(ll x,ll y,ll Mo) {
    x%=Mo;y%=Mo;
    ll tmp=(ll)((long double)x*y/Mo+1e-8)*Mo;
    return (x*y-tmp+Mo)%Mo;
}

ll pwr(ll x,ll y,ll Mo) {
    ll z=1;x%=Mo;y%=(Mo-1);
    for(;y;y>>=1,x=mult(x,x,Mo))
        if (y&1) z=mult(z,x,Mo);
    return z;
}

ll gcd(ll x,ll y) {
    if (!x) return 1;
    if (x<0) return gcd(-x,y);
    return y?gcd(y,x%y):x;
}

ll n,m,ans,nn,mm,invm;
ll pri[70],a[20];
int tot,cnt,b[20],tmp;


bool check(ll a,ll n,ll x,ll t) {
    ll res=pwr(a,x,n),lst=res;
    fo(i,1,t) {
        res=mult(res,res,n);
        if (res==1&&lst!=1&&lst!=n-1) return 1;
        lst=res;
    }
    if (res!=1) return 1;
    else return 0;
} 

bool Miller_Rabin(ll n) {
    if (n<2) return 0;
    if (n==2) return 1;
    if (!(n&1)) return 0;
    ll x=n-1,t=0;
    while (!(x&1)) x>>=1,t++;
    fo(i,1,S) {
        ll a=rand()%(n-1)+1;
        if (check(a,n,x,t)) return 0;
    }
    return 1;
}

ll Pollard_rho(ll n,ll t) {
    int i=1,k=2;
    ll x0=rand()%n,y=x0;
    while (1) {
        i++;
        x0=(mult(x0,x0,n)+t)%n;
        ll d=gcd(y-x0,n);
        if(d!=1&&d!=n) return d;  
        if(y==x0) return n;  
        if(i==k) y=x0,k<<=1;  
        tmp++;
    }  
}

void find(ll n) {
    if (n==1) return;
    if (Miller_Rabin(n)) {
        pri[++tot]=n;
        return;
    }
    ll p=n;tmp=0;
    while (p>=n) p=Pollard_rho(p,rand()%(n-1)+1);
    printf("%d\n",tmp);
    find(p);find(n/p);
}

void dfs(int x,ll y,ll phi) {
    if (x>cnt) {
        (ans+=gcd(y,m)%Mo*pwr(m,n/y,Mo)%Mo*phi%Mo)%=Mo;
        return;
    }
    ll z=1,p=1;
    fo(i,0,b[x]) {
        dfs(x+1,y*z,phi*p%Mo);
        z=z*a[x];
        if (i==0) p=p*(a[x]-1)%Mo;
        else p=p*a[x]%Mo;
    }
}

int ty;

int main() {
    freopen("necklace.in","r",stdin);
    freopen("necklace.out","w",stdout);
    ll inv2=pwr(2,Mo-2,Mo);srand(233333);
    for(scanf("%d",&ty);ty;ty--) {
        scanf("%lld%lld",&n,&m);
        nn=n%Mo;mm=m%Mo;
        if (n&1) ans=pwr(m,n/2+1,Mo)*nn%Mo;
        else {
            if (m&1) ans=pwr(m,n/2,Mo)*nn%Mo*inv2%Mo;
            else ans=pwr(m,n/2,Mo)*nn%Mo;
            (ans+=pwr(m,n/2+1,Mo)*nn%Mo*inv2%Mo)%=Mo;
        }
        tot=cnt=0;find(n);
        sort(pri+1,pri+tot+1);
        fo(i,1,tot) 
            if (pri[i]!=pri[i-1]) a[++cnt]=pri[i],b[cnt]=1;
            else b[cnt]++;
        dfs(1,1,1);
        ans=ans*pwr(nn*mm*2%Mo,Mo-2,Mo)%Mo;
        printf("%d\n",ans);
    }
}

你可能感兴趣的:(置换群,burnside引理)