小C的利是 题解

一、题目:



二、思路:

这道题思路非常妙😄,我们先理清思路,然后再来说这种思路为什么妙。

先考虑如果 \(a\) 中没有 \(-1\) 的情况。

第一步,找到一个质数 \(P\),满足 \(P\equiv 1\pmod K\)。即 \(K|(P-1)\)

第二步,找到模 \(P\) 的一个原根 \(g'\)

第三步,令 \(g=(g')^{(P-1)/K}\)。注意,此时我们求出的 \(g\) 是模 \(P\) 意义下的 \(K\) 次单位根。这里的单位根可以类比复数中的单位根。

第四步,对于每个 \(i\in[0,K-1]\),令 \(x_i=g^i\),构造如下矩阵:

\[A_i=\begin{bmatrix}b_{1,1}\times x_i^{a_{1,1}}&b_{1,2}\times x_i^{a_{1,2}}&\cdots&b_{1,n}\times x_i^{a_{1,n}}\\b_{2,1}\times x_i^{a_{2,1}}&b_{2,2}\times x_i^{a_{2,2}}&\cdots&b_{2,n}\times x_i^{a_{2,n}}\\ \vdots & \vdots &\ddots&\vdots\\ b_{n,1}\times x_i^{a_{n,1}} & b_{n,2}\times x_i^{a_{n,2}}&\cdots & b_{n,n}\times x_i^{a_{n,n}} \end{bmatrix} \]

其中,\(b\) 是随机生成的一个矩阵。如果一个位置的 \(a\)\(-1\),那么就把对应位置上的值修改为 \(0\)

第五步,令 \(s=\sum\limits_{i=0}^{K-1}\det (A_i)\)。若 \(s\neq 0\),则输出 Yes,否则输出 No

这样做为什么是正确的呢?

考虑一个排列 \(p\)\(s\) 的贡献。设 \(S=\sum\limits_{i=1}^n a_{i,p_i}\)。则它对 \(s\) 的贡献就是

\[\left(\prod\limits_{i=1}^n b_{i,p_i}\right)(-1)^{\tau(p)}(x_0^S+x_1^S+\cdots+x_{K-1}^S) \]

其中,前面的那一大坨是一个无关紧要的数,我们设为常数 \(C\)

则上式为

\[C\times(g^0+g^S+g^{2S}+\cdots+g^{(K-1)S}) \]

这是一个等比数列。

我们发现,当 \(S\equiv 0\pmod K\) 时,上式在模 \(P\) 意义下等于 \(C\times K\)

\(S\not\equiv0\pmod K\) 时,上式用等比数列求和公式,可得 \(C\times \dfrac{g^{KS}-1}{g^S-1}\)。由于 \(g^K\equiv1\pmod P\),所以上式等于 \(0\)

所以,只要存在排列 \(p\),使得 \(S\equiv 0\pmod K\)\(s\) 在很大概率上就不会等于 \(0\)。反之,\(s\) 就一定等于 \(0\)

现在看出来了吧,\(b\) 这个矩阵的作用就是使得我们检验正确的概率加大。

这道题妙就妙在,它运用了行列式可以遍历所有排列的特点,而且我们又可以在 \(O(n^3)\) 的时间内求出行列式的值。

三、代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <ctime>

using namespace std;
#define FILEIN(s) freopen(s".in", "r", stdin);
#define FILEOUT(s) freopen(s".out", "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)

#define int long long

inline int read(void) {
    int x = 0, f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return f * x;
}

const int maxn = 105;

int n, K, P, g, X, b[maxn][maxn], a[maxn][maxn];
int num[maxn][maxn];

inline bool check(int x) {
    if (x == 1) return false;
    if (x == 2) return true;
    for (int i = 2; i * i <= x; ++ i) 
        if (x % i == 0) return false;
    return true;
}

void find(void) {
    for (int i = 1; ; ++ i) 
        if (check(i * K + 1)) {
            P = i * K + 1; return;
        }
}

inline int power(int a, int b, int p) {
    int res = 1;
    for (; b; b >>= 1) {
        if (b & 1) res = res * a % p;
        a = a * a % p;
    }
    return res;
}

void find_root(void) {
    g = 2;
    for (; ; ++ g) {
        bool flag = true;
        for (int i = 2; i * i <= P - 1; ++ i) {
            if ((P - 1) % i == 0) {
                if (power(g, i, P) == 1) { flag = false; break; }
                if (power(g, (P - 1) / i, P) == 1) { flag = false; break; }
            }
        }
        if (flag) break;
    }
    g = power(g, (P - 1) / K, P);
}

void build(void) {
    for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= n; ++ j) {
            if (a[i][j] == -1) num[i][j] = 0;
            else {
                num[i][j] = b[i][j] * power(X, a[i][j], P) % P;
            }
        }
    }
}

inline int Gauss(void) {
    int res = 1;
    for (int i = 1; i <= n; ++ i) {
        for (int j = i + 1; j <= n; ++ j) {
            while (num[i][i]) {
                int r = num[j][i] / num[i][i];
                for (int k = i; k <= n; ++ k) {
                    num[j][k] = (num[j][k] - r * num[i][k] % P + P) % P;
                }
                res = -res;
                swap(num[i], num[j]);
            }
            swap(num[i], num[j]); res = -res;
        }
    }
    for (int i = 1; i <= n; ++ i) {
        res = res * num[i][i] % P;
    }
    (res += P) %= P;
    return res;
}

signed main() {
    FILEIN("luckymoney"); FILEOUT("luckymoney");
    srand((unsigned)time(0));
    n = read(); K = read();
    for (int i = 1; i <= n; ++ i) {
        for (int j = 1; j <= n; ++ j) {
            a[i][j] = read();
            b[i][j] = rand() + 1;
        }
    }
    find();
    find_root();
    int res = 0;
    for (int i = 0; i <= K - 1; ++ i) {
        X = power(g, i, P);
        build();
        (res += Gauss()) %= P;
    }
    if (res) puts("Yes");
    else puts("No");
    return 0;
}
posted @ 2021-06-05 08:53  蓝田日暖玉生烟  阅读(101)  评论(3编辑  收藏  举报