P3758 [TJOI2017]可乐

3 篇文章 0 订阅
3 篇文章 0 订阅

题目描述

加里敦星球的人们特别喜欢喝可乐。因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的1号城市上。这个可乐机器人有三种行为: 停在原地,去下一个相邻的城市,自爆。它每一秒都会随机触发一种行为。现 在给加里敦星球城市图,在第0秒时可乐机器人在1号城市,问经过了t秒,可乐机器人的行为方案数是多少?

输入输出格式

输入格式:

第一行输入两个正整数况N,M,N表示城市个数,M表示道路个数。(1 <= N <=30,0 < M < 100)

接下来M行输入u,v,表示u,v之间有一条道路。(1<=u,v <= n)保证两座城市之间只有一条路相连。

最后输入入时间t

输出格式:

输出可乐机器人的行为方案数,答案可能很大,请输出对2017取模后的结果。

 对于20%的pn,有1 < t ≤ 1000

对于100%的pn,有1 < t ≤ 10^6。

想法:

是一道很显然的dp题。转移方程分为爆炸和不爆炸两个。

f[0][i][j]表示在第i个时刻机器人在节点j上 并且已经爆炸了(可以是任何一个时间爆炸的)f[1][i][j]表示没有爆炸

f[0][i][j]=f[0][i-1][j]+f[1][i-1][j] 显然它要么是这个时刻自爆的 要么是已经爆了

f[1][i][j]=f[1][i-1][j]+f[1][i-1][k] 要么是上一步选择了原地不动 要么是从周围的某个点走过来的 其中k表示和j相邻的点

但是直接这样做不行 会MLE或者TLE。反正只有20分。所以要想办法优化。

有个好东西叫做矩阵快速幂。

我们可以把j和自己也想像成联通的 这样第二个式子就变成了f[1][i][j]=sum(f[1][i-1][k])。之后利用矩阵快速幂就可以直接算出最后一项的值。(相当于是原来那个图的邻接矩阵 再加上自己和自己的边)

之后再看第一个式子f[0][i][j]=f[0][i-2][j]+f[1][i-2][j]+f[1][i-1][j] 发现是一个等比数列求和。那么在刚才那个矩阵上的第n+1列全部填上1就好了。

最后的答案就是对结果矩阵的每个值求和。

代码:
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;
const int p=2017;
int n,m,t;
struct matr
{
    int x,y,sum;
    long long m[35][35];
}a,b;
matr operator *(matr a,matr b)
{
    matr c;
    c.x=a.x;c.y=b.y;
    c.sum=0;
    for(int i=1;i<=c.x;i++)
    {
        for(int j=1;j<=c.y;j++)
        {
            c.m[i][j]=0;
            for(int k=1;k<=a.x;k++)
            {
                c.m[i][j]+=(a.m[k][j]%p)*(b.m[i][k]%p);
                c.m[i][j]%=p;
            }
            c.sum+=c.m[i][j];
            c.sum%=2017;
        }
    }
    return c;
}
int main()
{
    scanf("%d%d",&n,&m);
    a.x=n+1,a.y=1;
    a.sum=b.sum=0;
    a.m[1][1]=1;
    b.x=b.y=n+1;
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        b.m[u][v]=1;
        b.m[v][u]=1;
    }
    for(int i=1;i<=n+1;i++)
    {
        b.m[i][i]=1;
        b.m[n+1][i]=1;
    }
    scanf("%d",&t);
    while(t)
    {
        if(t&1) a=a*b;
        b=b*b;
        t=(t>>1);
    }
    printf("%d\n",a.sum);
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值