zoukankan      html  css  js  c++  java
  • 2019 icpc 南昌 J.Summon

    应该能想到polya定理,考虑一下polya定理的公式是怎么一个表达。

    [frac{1}{|G|}sum_{fin G}m^{m(f)} ]

    (f)是一个置换,(m(f))是置换的循环的数量。

    因为有对限制,所以这就导致了这道题不能直接做,我们从原理入手。

    考虑置换群(G={0,1,...,n-1})。我们知道对于一个置换(L),有循环数量为(gcd(L,n)),而且第一个元素在第一个循环中,第二个元素在第二个循环中,...,第(gcd(L,n))个元素在第(gcd(L,n))个循环中,而且每个循环中的元素颜色相同。

    所以问题可以抽象为一个链条长度为(gcd(L,n)),且在上面染色的计数问题。

    接下来我们处理这样的一个问题。

    如果考虑是两个限制的话,即((a,b))表示这两个颜色不能相邻,实际题目中是四个,先考虑简单的。

    我们可以用一个矩阵(m(i,j)=1)表示(i,j)两色可以相邻,否则不行。

    然后我们可以写一个(dp)方程,设(f(i,j))表示处理到前(i)位最后一位颜色是(j)的答案,转移方程:

    [f(i,j)=sum f(i-1,k) imes m(k,j) ]

    当然要保证最后一位的颜色和第一位的颜色满足条件,所以我们可以取:

    [sum f(gcd(L,n)+1, j) ]

    为答案,就相当于是枚举第一个元素的颜色(j),你在尾部接一个(j),到这里一定是满足条件的。

    为什么说这个呢,在后面的推导中用到了这样的(dp)思想,但实际上用的是矩阵快速幂。

    在离散数学中,如果用(0,1)矩阵表示无向图连通情况的话,(A^k(i,j))次方代表的就是一个点经过(k)条路(也就是(k)次转移,到了第(k+1)位)后(i)点到(j)点的方案数,又由于我们要起始元素颜色和终止元素颜色相同。

    • 所以矩阵此时的对角线之和就是答案。

    考虑一个四元组((a,b,c,d))

    如果第一位是(a)且后面跟着(b,c),那么第二位就不能是(b)且后面跟着(c,d)

    这样就抽象到二维了,也就是上面的那个问题。

    所以我们就设矩阵(m(i,j))表示(i)后面跟着(x_1,x_2)(j)后面跟着(y_1,y_2)是否可行。

    这样的(i,j)就分别表示一个三元组,跑矩阵快速幂就是答案。

    设对于长度为(i)的链条,方案数为(S(i))

    那么我的答案就是:

    [sum_{L=1}^nS(gcd(L,n)) ]

    枚举(gcd(L,n)=d)

    预处理矩阵的阶乘,在快速幂的时候不要频繁的调动(a=a*a\%mod)

    最后别忘了除以(n),直接乘上(n)(mod 998244353)意义下的逆元。

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int mod = 998244353;
    const int maxn = 1e5+10;
    int primes[maxn], cnt;
    int phi[maxn];
    bool v[maxn];
    void init(int n)
    {
        phi[1] = 1;
        for(int i = 2; i <= n; i++)
        {
            if(!v[i])
            {
                primes[++cnt] = i;
                phi[i] = i-1;
            }
            for(int j = 1; primes[j] <= n/i; j++)
            {
                v[primes[j]*i] = 1;
                if(i%primes[j] == 0)
                {
                    phi[primes[j]*i] = phi[i]*primes[j];
                    break;
                }
                else phi[primes[j]*i] = phi[i]*(primes[j]-1);
            }
        }
    }
    
    struct mat
    {
        ll a[67][67];
    }base, pbase[25];
    
    mat mul(mat a, mat b)
    {
        mat res;
        memset(res.a, 0, sizeof res.a);
        for(int i = 0; i < 64; i++)
        for(int j = 0; j < 64; j++)
        for(int k = 0; k < 64; k++)
        res.a[i][j] = (res.a[i][j]%mod+a.a[i][k]%mod*b.a[k][j]%mod)%mod;
        return res;
    }
    
    ll qmi(ll a, ll b)
    {
        ll res = 1;
        while(b)
        {
            if(b&1) res = (res*a)%mod;
            a = a*a%mod;
            b >>= 1;
        }
        return res%mod;
    }
    
    
    mat qmi(int b)
    {
        mat res; memset(res.a, 0, sizeof res.a);
        for(int i = 0; i < 64; i++) res.a[i][i] = 1;
        int t = 0;
        while(b)
        {
            if(b&1) res = mul(res, pbase[t]);
            b >>= 1; t++;
        }
        return res;
    }
    
    int main()
    {
        init(maxn-5);
        int n, m;
        scanf("%d%d", &n, &m);
        for(int i = 0; i < 64; i++)
            for(int k = 0; k < 4; k++)
        {
            int nxt = (i*4+k)%64;
            base.a[i][nxt] = 1;
        }
    
        while(m--)
        {
            int x = 0, t;
            for(int i = 0; i < 4; i++)
            {
                scanf("%d", &t);
                x = x*4+t;
            }
            base.a[x/4][x%64] = 0;
        }
        pbase[0] = base;
        for(int i = 1; i < 20; i++) pbase[i] = mul(pbase[i-1], pbase[i-1]);
    
    
        ll ans  = 0;
        for(int i = 1; i <= n; i++)
        {
            if(n%i) continue;
            mat tt = qmi(i);
            ll t = 0;
            for(int j = 0; j < 64; j++)
                t = (t+tt.a[j][j])%mod;
            t = t*phi[n/i]%mod;
            ans = (ans+t)%mod;
        }
        ans = ans%mod*qmi(n, mod-2)%mod;
        cout << ans << endl;
    
        return 0;
    }
    
    
  • 相关阅读:
    Halcon一日一练:图像分割之基本概念
    Halcon一日一练:创建AOI
    Halcon一日一练:获取图像属性
    Halcon一日一练:创建三通道图像
    Halcon一日一练:Halcon异常判断方法
    Java基础_类的加载机制和反射
    Java基础_死锁、线程组、定时器Timer
    Java基础_通过模拟售票情景解决线程不安全问题
    Java基础_线程的使用及创建线程的三种方法
    HTML/HTML5 知识点思维导图
  • 原文地址:https://www.cnblogs.com/zxytxdy/p/12582065.html
Copyright © 2011-2022 走看看