BZOJ 1898 ZJOI 2004 Swamp 沼泽鳄鱼 矩阵乘法

题目大意

给出一张无向图,这个图中有一些鱼,他们不同的时间会出现在固定的位置,呈周期性循环,一个人要在这个图上走,他不能和鱼同时在一个点上。问从s到t走k步有多少种方案。

思路

注意到鱼的循环只可能是2/3/4,也就是说最多经过12个时间点之后,状态又会和一开始相同。所以预处理12个矩阵用来转移。分为k/12和k%12来处理。
当鱼在一个位置上的时候,当前时间从这个位置出发的一行和上一个时间到达这个点的一列需要清零。

CODE

#define _CRT_SECURE_NO_WARNINGS

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 60
#define MO 10000
using namespace std;

int points;

struct Matrix{
    int num[MAX][MAX];
    int w,h;

    Matrix(int _,int __):w(_),h(__) {
        memset(num,0,sizeof(num));
    }
    Matrix() {
        memset(num,0,sizeof(num));
    }
    Matrix operator *(const Matrix &a)const {
        Matrix re(w,a.h);
        for(int i = 1; i <= w; ++i)
            for(int j = 1; j <= a.h; ++j) {
                for(int k = 1; k <= h; ++k)
                    re.num[i][j] += num[i][k] * a.num[k][j];
                re.num[i][j] %= MO;
            }
        return re;
    }
    void ClearHor(int line) {
        for(int i = 1; i <= points; ++i)
            num[line][i] = 0;
    }
    void ClearVer(int line) {
        for(int i = 1; i <= points; ++i)
            num[i][line] = 0;
    }
}src[20];

struct Fish{
    int cnt,num[10];

    void Read() {
        scanf("%d",&cnt);
        for(int i = 0; i < cnt; ++i)
            scanf("%d",&num[i]),++num[i];
    }
}fish[MAX];

int edges,s,t;
long long k;
int fishes;

Matrix QuickPower(Matrix a,long long y)
{
    Matrix re(points,points);
    for(int i = 1; i <= points; ++i)
        re.num[i][i] = 1;
    while(y) {
        if(y&1) re = re * a;
        a = a * a;
        y >>= 1;
    }
    return re;
}

int main()
{
    cin >> points >> edges >> s >> t >> k;
    ++s,++t;
    Matrix map(points,points);
    for(int x,y,i = 1; i <= edges; ++i) {
        scanf("%d%d",&x,&y);
        ++x,++y;
        map.num[x][y] = map.num[y][x] = 1;
    }
    for(int i = 0; i < 12; ++i)
        src[i] = map;
    cin >> fishes;
    for(int i = 1; i <= fishes; ++i)
        fish[i].Read();
    for(int i = 0; i < 12; ++i) {
        for(int j = 1; j <= fishes; ++j) {
            int pos = fish[j].num[i % fish[j].cnt];
            if(i)   
                src[i - 1].ClearVer(pos);
            src[i].ClearHor(pos);
        }
    }
    Matrix base(points,points);
    for(int i = 1; i <= points; ++i)
        base.num[i][i] = 1;
    for(int i = 0; i < 12; ++i)
        base = base * src[i];
    Matrix ans = QuickPower(base,k / 12);
    k %= 12;
    for(int i = 0; i < k; ++i)
        ans = ans * src[i];
    cout << ans.num[s][t] << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值