[BZOJ]1002: [FJOI2007]轮状病毒(Matrix-tree定理+高精度加减乘除;直接递推)

1002: [FJOI2007]轮状病毒
  • 题意就是一个 matrix-tree

  • 但因为这里没给模数,所以我们不能欧几里得。。。

  • 于是我打上了高精度加减乘,特么还要资瓷负数,结果就是这么长的代码了:

#include <iostream>
#include <cstring>
#include <cstdio>

#define F(i, a, b) for (int i = a; i <= b; i ++)
#define G(i, a, b) for (int i = a; i >= b; i --)
#define mem(a, b) memset(a, b, sizeof a)
#define mec(a, b) memcpy(a, b, sizeof a)
#define N 110

using namespace std;

int n; struct array { int a[N >> 1], bz; } C[N][N], tmp, ans, div, d;

bool cmp(int *a, int *b) {
    if (a[0] ^ b[0]) return a[0] > b[0];
    G(i, a[0], 1)
        if (a[i] ^ b[i]) return a[i] > b[i];
    return 1;
}

bool cmq(int *a, int *b) {
    int c[N]; mec(c, a);
    F(i, 1, c[0]) c[i] = a[a[0] - i + 1];
    return cmp(c, b);
}

void Down(array &a, array b) {
    F(i, 1, b.a[0]) {
        a.a[i] = a.a[i] - b.a[i];
        if (a.a[i] < 0) a.a[i] += 10, a.a[i + 1] --;
    }
    while (a.a[0] && !a.a[a.a[0]]) a.a[0] --;
}

void Add(array &a, array b) { a.a[0] = max(a.a[0], b.a[0]);
    for (int i = 1; i <= a.a[0]; i ++) {
        a.a[i] = a.a[i] + b.a[i];
        a.a[i + 1] += a.a[i] / 10;
        a.a[i] %= 10;
        if (i == a.a[0] && a.a[i + 1]) a.a[0] ++;
    }
}

void Mul(array &a, int *b) {
    array c; mem(c.a, 0);
    F(i, 1, a.a[0])
        F(j, 1, b[0]) {
            c.a[i + j - 1] += a.a[i] * b[j];
            c.a[i + j] += c.a[i + j - 1] / 10;
            c.a[i + j - 1] %= 10;
        }
    c.a[0] = a.a[0] + b[0];
    while (c.a[0] && !c.a[c.a[0]]) c.a[0] --;
    mec(a.a, c.a);
}

int main() {
    scanf("%d", &n), ans.a[ans.a[0] = 1] = 1;
    F(i, 1, n) {
        C[i][i % n + 1].a[0] = C[i][(i - 2 + n) % n + 1].a[0] = C[i][i].a[0] = 1;
        C[i][i].a[1] = 3, C[i][i % n + 1].a[1] = C[i][(i - 2 + n) % n + 1].a[1] = 1;
        C[i][i % n + 1].bz = C[i][(i - 2 + n) % n + 1].bz = 1;
    }
    
    F(i, 1, n) {
        int now = i;
        F(j, i, n)
            if (C[j][i].a[0]) { now =j; break; }

        F(j, now + 1, n)
            while (C[j][i].a[0]) {
                mem(div.a, 0), mem(d.a, 0), div.bz = (C[now][i].bz ^ C[j][i].bz) ? 1 : 0;
                G(k, C[j][i].a[0], 1) {
                    if (C[j][i].a[k] || d.a[0]) d.a[++ d.a[0]] = C[j][i].a[k];
                    int cnt = 0;
                    while (cmq(d.a, C[now][i].a)) {
                        int L = C[now][i].a[0];
                        G(p, d.a[0] - L + 1, 1) {
                            d.a[p] -= C[now][i].a[L - p + 1];
                            if (d.a[p] < 0) d.a[p] += 10, d.a[p - 1] --;
                        }
                        cnt ++;
                        while (d.a[0] && !d.a[d.a[0]]) d.a[0] --;
                    }
                    if (div.a[0] || cnt) {
                        div.a[++ div.a[0]] = cnt % 10;
                        if (cnt > 9) div.a[++ div.a[0]] = cnt / 10;
                    }
                }
                
                tmp = div;
                F(i, 1, div.a[0]) div.a[i] = tmp.a[div.a[0] - i + 1];

                F(k, 1, n) {
                    tmp = div, tmp.bz ^= C[now][k].bz, Mul(tmp, C[now][k].a);
                    if (tmp.bz ^ C[j][k].bz) Add(C[j][k], tmp); else {
                        if (cmp(C[j][k].a, tmp.a)) Down(C[j][k], tmp); else Down(tmp, C[j][k]), C[j][k] = tmp, C[j][k].bz = tmp.bz == 0;
                    }
                }

                if (!C[j][i].a[0]) break;
                
                F(k, 1, n)
                    swap(C[j][k], C[now][k]);
            }
        
        Mul(ans, C[i][i].a);
    }

    G(i, ans.a[0], 1) printf("%d", ans.a[i]);
}
  • 看到这么恶心的做法,就知道这题肯定还有其他的做法!!

  • 具体思路就是根据行列式的定义去展开,然后用\(f[n]\)表示当行列式为\(n*n\)的方案数,每次展开以后得到类似的\(f[n-1],f[n-2]\),然后再求解一下.

  • 再具体一点,就是这样:https://www.cnblogs.com/Parry-PY/p/7731858.html

猜你喜欢

转载自www.cnblogs.com/Pro-king/p/9383437.html
今日推荐