BZOJ1898. [Zjoi2005]Swamp 沼泽鳄鱼

机房大佬讲解后, 写一写题解

这是很经典的邻接矩阵乘法了

对于邻接矩阵G[i][j] 表示i到j有没有边

如果是\(G^1\) , 发现就是原矩阵, G[i][j]可以理解为从i走一步到j的方案数

下面考虑\(G^2\)

矩阵乘法代码:

Matrix operator * (const Martix &p) const{
        Martix tmp;
        for (rint i = 0;i < n; i++) 
            for (rint j = 0;j < n; j++) 
                for (rint k = 0;k < n; k++) 
                    tmp.Mar[i][j] = (tmp.Mar[i][j] + Mar[i][k] * p.Mar[k][j]) % P;
        return tmp;
    }

发现\(G^2[i][j] = \sum_{k=0}^{n-1} G[i][k] * G[k][j]\) 像不像Floyd

即枚举一个中间点k, 从i到j走两步的的方案数等于从i到走一步k的方案数, 再乘上从k走一步到j的方案数之和

以此类推

\(G^t[i][j]\)为再G这个邻接矩阵上从i走t步到j的方案数

原矩阵\(G^0\)为单位矩阵, 每乘一个新的邻接矩阵相当于又在新的邻接矩阵上走一步

回到本题

2, 3, 4的最小公倍数为12

所以开十二个邻接矩阵即可, 每个矩阵表示当前走一步的合法情况

a[0]表示走12步的矩阵, 即12个邻接矩阵之积

k/12个a[0]相乘使用快速幂, 在按顺序从a[1]乘到a[k%12]

答案为 \(a[0]^{k/12} * a[1] * a[2] * \cdots s[k\%12]\)

细节见代码及注释:

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
template <typename T> 
void read(T &x) {
    x = 0; int f = 1;
    char c = getchar();
    for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
    for (;isdigit(c);c=getchar()) x = (x << 3) + (x << 1) + c - '0';
    x *= f;
}//快读
const int N = 52;
int n, m;
ll t, k;
const int P = 10000;
#define rint register int
struct matrix{
    int Mar[N][N];
    matrix () {
        memset(Mar, 0, sizeof(Mar));
    }
    matrix operator * (const matrix &p) const{
        matrix tmp;
        for (rint i = 0;i < n; i++) 
            for (rint j = 0;j < n; j++) 
                for (rint k = 0;k < n; k++) 
                    tmp.Mar[i][j] = (tmp.Mar[i][j] + Mar[i][k] * p.Mar[k][j]) % P;
        return tmp;
    }//矩阵乘法
    void print(void) {
        for (int i = 0;i < n; i++) {
            for (int j = 0;j < n; j++) 
                printf ("%d ", Mar[i][j]);
            cout << endl;
        }
        cout << endl;
    }//调试输出
}a[15], b, c;

matrix pow() {
    matrix ans = b, tmp = a[0];
    int tttmp = k / 12;
    while (tttmp) {
        if (tttmp & 1) ans = ans * tmp;
        tmp = tmp * tmp;
        tttmp >>= 1;
    }
    return ans;
}//矩阵快速幂
int nfish, ttmp[50], st, ed;            
int main() {
//  freopen("hs.out","w",stdout);
    read(n), read(m), read(st), read(ed), read(k);
    while (m--) {
        int x, y;
        read(x), read(y);
        c.Mar[x][y] = c.Mar[y][x] = 1; //原矩阵
    }
//  c.print();
    for (rint i = 1;i <= 12; i++) a[i] = c; //初始化
    for (rint i = 0;i < n; i++) b.Mar[i][i] = 1; //单位矩阵
    read(nfish);
    while (nfish--) {
        read(t);
        for (int i = 0;i < t; i++) read(ttmp[i]); ttmp[t] = ttmp[0]; 
            //由于开始时时间为0, 所以读入要从零开始
        for (int i = 1;i <= 12; i++) 
            for (int j = 0;j < n; j++) 
                a[i].Mar[j][ttmp[(i-1)%t+1]] = 0; 
                //注意只清空从其他地方到鳄鱼新来的地点的边
    }
//  for (int i = 1;i <= 12; i++) a[i].print();
    a[0] = b;
    for (int i = 1;i <= 12; i++) a[0] = a[0] * a[i];    
//  a[0].print();
    matrix ans = pow();
    for (int i = 1;i <= k % 12; i++) ans = ans * a[i];
    cout << ans.Mar[st][ed] << endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Hs-black/p/11756425.html
今日推荐