[ZJOI2005]沼泽鳄鱼

1 篇文章 0 订阅
1 篇文章 0 订阅

今天继续攻集训队论文的我

题意

一张无向图,不超过 50 50 个点
起点 s s 终点 t
一个单位时间移动一次
一些食人鱼作周期运动 长度不超过 4 4
人不能碰到食人鱼
时刻 u 到达终点 u<=2109 u <= 2 ∗ 10 9

分析

引理

我们观察数据发现 n n 十分的小,那么就可以直接用邻接矩阵
而且有一种矩阵叫矩阵乘法
对于一张(无向)图:G

G[i][j]=1i>j G [ i ] [ j ] = 1 表 示 i − > j 有 一 条 边

那么我们想一想 G2 G 2 表示的意义是什么:
G[a][b]=k=0NG[a][k]G[k][b] G ′ [ a ] [ b ] = ∑ k = 0 N G [ a ] [ k ] ∗ G [ k ] [ b ]

那么是不是就是表示 2a>b 走 2 布 的 时 候 a − > b 的 方 案 数 ,那么以此类推
k k 布的方案数就是Gk了!

此题分析

我们用 Ak A k 表示走 k k 布的情况下的方案数
Gi表示在走第 i i 布是的图的情况(0,1)表示
那么如果没有食人鱼的话就是:

Ak=Gk

Gu=Gu1Gk G u = G u − 1 ∗ G k

那么如果有食人鱼的话就是
Ak=Ak1Gk A k = A k − 1 ∗ G k

那么我们看见食人鱼的循环是 2,3,4 2 , 3 , 4 不等,但是 lcm(2,3,4)=12 l c m ( 2 , 3 , 4 ) = 12
也就是意味着 G1=G13 G 1 = G 13 这两张图是一样的
那么我们可以 O(n3) O ( n 3 ) 预处理出add矩阵
add=k=012Gk a d d = ∏ k = 0 12 G k

a=k/12,b=k a = k / 12 , b = k

那么答案的矩阵就是:
final=pow(add,a)1bGi f i n a l = p o w ( a d d , a ) ∗ ∏ 1 b G i 记 住 矩 阵 乘 法 的 左 乘 和 右 乘 是 不 一 样 的

复杂度 O(n3log2q) O ( n 3 l o g 2 q )

代码:

#pragma GCC optimize(3)
#pragma GCC optimize("Ofast")
#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 55
#define M 25
#define R register
using namespace std;

int n, m, s, t, K, x, y, Nfish, Pow_time, leftn, ff[N], att[N][10];

struct matrix
{
    int a[N][N];
    matrix()
    {
        memset(a, 0, sizeof a);
    }
} G[20], fuu, ans, fff;

inline matrix operator * (matrix x, matrix y)
{
    matrix z;
    for(R int i = 0; i < n; ++i)
        for(R int j = 0; j < n; ++j)
            for(R int k = 0; k < n; ++k)
            {
                z.a[i][j] += x.a[i][k] * y.a[k][j];
                if(z.a[i][j] > 10000) z.a[i][j] %= 10000;
            }
    return z;
}


inline matrix Pow(matrix now, int y)
{
    matrix res;
    for(R int i = 0; i < n; ++i) res.a[i][i] = 1;
    while(y)
    {
        if(y & 1) res = res * now;
        now = now * now;
        y >>= 1;
    }
    return res;
}

inline void init()
{
    for(R int i = 1; i <= 12; i++)
    {
        G[i] = fuu;
        for(R int j = 1; j <= Nfish; j++)
        {
            int to = att[j][((i + 1) % ff[j]) ? ((i + 1) % ff[j]) : ff[j]];
            for(R int k = 0; k < n; k++)
                G[i].a[k][to] = 0;
        }
    }
}

inline matrix solve()
{
    Pow_time = K / 12;
    leftn = K % 12;
    matrix ans, fff;
    for(R int i = 0; i < n; ++i) ans.a[i][i] = fff.a[i][i] = 1;
    for(R int i = 1; i <= 12; ++i) fff = fff * G[i];
    ans = ans * Pow(fff, Pow_time);
    for(R int i = 1; i <= leftn; ++i)
    {
        ans = ans * G[i];
    }
    return ans;
}

inline int read()
{
    int x=0;
    char c=getchar();
    bool flag=0;
    while(c<'0'||c>'9'){if(c=='-')flag=1;   c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    return flag?-x:x;
}

int main()
{
    n = read(), m = read(), s = read(), t = read(), K = read();
    for(R int i = 1; i <= m; ++i)
    {
        x = read(), y = read();
        fuu.a[x][y] = fuu.a[y][x] = 1;
    }
    Nfish = read();
    for(R int i = 1; i <= Nfish; ++i)
    {
        ff[i] = read();
        for(int j = 1; j <= ff[i]; ++j)
            att[i][j] = read();
    }
    init();
    ans = solve();
    printf("%d\n", ans.a[s][t]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值