洛谷 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\)

浙公网安备 33010602011771号