洛谷 P5789 [TJOI2017] 可乐——题解

洛谷P5789题解


传送锚点


题目

P5789 [TJOI2017] 可乐(数据加强版)

题目背景

原题 数据很弱,这个加强版卡掉了暴力的 DP 做法,并且补充了原题题面中缺少的 \(\LaTeX\)

题目描述

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

输入格式

第一行输入两个正整数 \(N,M\)\(N\) 表示城市个数,\(M\) 表示道路个数。

接下来 \(M\) 行输入 \(u,v\) ,表示 \(u,v\) 之间有一条双向道路。

最后输入时间 \(t\)

输出格式

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

输入输出样例 #1

输入 #1

3 2
1 2
2 3
2

输出 #1

8

说明/提示

【数据规模与约定】

对于 \(20\%\) 的数据, \(n,m\leq 30\)\(t\leq 1000\)

对于 \(50\%\) 的数据, \(t\leq 10^6\)

对于 \(100\%\) 的数据, \(n,m\leq 100\) , $ t\leq 10^9$ .

【样例解释】

\(1\) -> 爆炸

\(1\) -> \(1\) -> 爆炸

\(1\) -> \(2\) -> 爆炸

\(1\) -> \(1\) -> \(1\)

\(1\) -> \(1\) -> \(2\)

\(1\) -> \(2\) -> \(1\)

\(1\) -> \(2\) -> \(2\)

\(1\) -> \(2\) -> \(3\)


乍一看似乎和矩阵快速幂关系不大,看上去更像是一道图论题。

但是注意到 $ n $ ,$ m $ 的范围很小。这样我们可以用邻接矩阵储存图。

考虑一个邻接矩阵 $ T $ ,那么我们来思考一下,$ T^{k} $ 有什么特殊的含义。此时注意到这样的一行代码

for(int i=0;i<=n;i++)
    for(int j=0;j<=n;j++)
        for(int k=0;k<=n;k++)c[i][j]=(c[i][j]+(a[i][k]*t[k][j])%2017)%2017;

这玩意长得就挺像那 $ Floyd $ 枚举中间点 $ k $ 的代码。

所以$ T^{k} $ 的含义就是从 $ i $ 到 $ j $ 走 $ k $ 步的方案数。

但这并不完美契合题目的要求,虽然待在原地可以用自环来解决,但是自爆这个鬼玩意该怎样解决呢?实际上,我们可以引入一个虚拟的点 $ 0 $ ,然后将所有点向 $ 0 $ 连一条单向边,这样自爆后就可以转移到 $ 0 $ 了。

最后的答案即为 $ \sum_{i=0}^{n} a_{1,i} $ 。


完整代码

#ifdef ONLINE_JUDGE 
#else 
#define Qiu_Cheng 
#endif 
#include <bits/stdc++.h> 
#define i8  __int128
#define int long long 
#define fuck inline
#define lb long double 
using namespace std; 
// typedef longlong ll; 
const int N=2e5+5,M=1e6+520,mod=1e9+7;
const int inf=INT_MAX,INF=1e9+7; 
// const int mod1=469762049,mod2=998244353,mod3=1004535809;
// const int G=3,Gi=332748118; 
// const int M=mod1*mod2;
fuck int read()
{
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-'){f=-1;}c=getchar();}
    while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c-'0');c=getchar();}
    return x*f;
}
fuck void write(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
int t[205][205],a[205][205];
int c[205][205];
int m,n;int T;
fuck void fc1()
{
    memset(c,0,sizeof(c));
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
            for(int k=0;k<=n;k++)c[i][j]=(c[i][j]+(a[i][k]*t[k][j])%2017)%2017;
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)a[i][j]=c[i][j];
}
fuck void fc2()
{
    memset(c,0,sizeof(c));
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)
            for(int k=0;k<=n;k++)c[i][j]=(c[i][j]+(t[i][k]*t[k][j])%2017)%2017;
    for(int i=0;i<=n;i++)
        for(int j=0;j<=n;j++)t[i][j]=c[i][j];
}
fuck void solve()
{
	cin>>n>>m;
    memset(a,0,sizeof(a));
    memset(t,0,sizeof(t));
    for(int i=1;i<=m;i++)
    {
        int u,v;cin>>u>>v;
        a[u][v]=a[v][u]=t[u][v]=t[v][u]=1;
    }
    cin>>T;T-=1;//由于初始了a数组,所以此处要减一
    for(int i=0;i<=n;i++)a[i][0]=t[i][0]=1,t[i][i]=a[i][i]=1;
    while(T)
    {
        if(T&1)fc1();
        fc2();
        T>>=1;
    }
    int ans=0;
    for(int i=0;i<=n;i++)ans=(ans+a[1][i])%2017;
    cout<<ans<<"\n";
	
}
signed main() 
{ 
#ifdef Qiu_Cheng 
    freopen("1.in","r",stdin); 
    freopen("1.out","w",stdout); 
#endif 
    // ios::sync_with_stdio(false); 
    // cin.tie(0); cout.tie(0); 
    // int fuckccf=read();
    // for(int i=1;i<=tot;i++)cout<<prime[i]<<"\n";
    // int QwQ=read();
    // while(QwQ--)solve(); 
    solve(); 
    return 0; 
}
//  6666   66666  666666 
// 6    6  6   6      6 
// 6    6  6666      6 
// 6    6  6  6    6 
//  6666   6   6  6666666

相关类型

更为有趣的拓展

真正意义上的用矩阵快速幂求 $ k $ 次走完的最短路。


完结收工!!!!!

个人主页

看完点赞,养成习惯

\(\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\Downarrow\)

posted @ 2025-05-21 19:03  Nightmares_oi  阅读(62)  评论(0)    收藏  举报