题目描述
加里敦星球的人们特别喜欢喝可乐。因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的(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%的数据,有(1<t≤1000)
对于100%的数据,有(1<t≤10^6)。
一开始看着数据范围还以为是一道卡常数题啊......
这个保证(t≤10^6),直觉不会想到(logt)啊QAQ
显然,如果这道题目是卡常数题的话,那就是一道很简单的小学生dp了
(f[i][j][0])表示第(i)时刻,在城市(j)爆炸的方案数,(f[i][j][1])表示第(i)时刻,在城市(j)不爆炸的方案数
(f[i][j][0]=f[i-1][j][0]+f[i-1][j][1]),(f[i][j][1]=f[i][j][1]+f[i][k][1])((k)是(j)的相邻结点)
这样是要MLE的,显然状态的答案只与前一状态相关,直接滚动即可
事实证明,这样只有20分
看起来这是一阶递推,考虑用矩乘来优化
先考虑(f[i][j][1]=f[i][j][1]+f[i][k][1]),可以发现,如果我们假设(i)结点和自己本身相邻的话,可以将前一项并到后一项当中
我们可以用一个(n*n)的矩阵(A)和(f[0][?][1])构成的矩阵(B)相乘得到,而(n*n)的矩阵是表示是否直接相邻的邻接矩阵,特别的(A[i][i]=1)
这样的话,我们对(A)矩阵快速幂即可
继续考虑(f[i][j][0]=f[i-1][j][0]+f[i-1][j][1]),我们可以发现,这其实是(sum f[i-1][j][1]),按照前一个幂的处理,可以发现,这其实是一个矩阵的等比数列
直接按照等比数列求个和就好了咯
时间复杂度(O(nlogn))
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<string>
#include<climits>
#include<vector>
#include<cmath>
#include<map>
#define LL long long
using namespace std;
inline char nc(){
static char buf[100000],*p1=buf,*p2=buf;
if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
return *p1++;
}
inline void read(int &x){
char c=nc();int b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline void read(LL &x){
char c=nc();LL b=1;
for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
inline int read(char *s)
{
char c=nc();int len=0;
for(;!((c>='A' && c<='Z')||(c>='a' && c<='z'));c=nc()) if (c==EOF) return 0;
for(;((c>='A' && c<='Z')||(c>='a' && c<='z'));s[len++]=c,c=nc());
s[len++]=' ';
return len;
}
inline void read(char &x){
for (x=nc();!(x=='?' || x=='+' || x=='-');x=nc());
}
int wt,ss[19];
inline void print(int x){
if (x<0) x=-x,putchar('-');
if (!x) putchar(48); else {
for (wt=0;x;ss[++wt]=x%10,x/=10);
for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
if (x<0) x=-x,putchar('-');
if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}
int n,m,T,f[40],g[40];
const int mo=2017;
struct data
{
int f[40][40];
}a,b;
void add(data &x,data y)
{
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
x.f[i][j]=(x.f[i][j]+y.f[i][j])%mo;
}
void mul(data &x,data y)
{
data z;
memset(z.f,0,sizeof(z.f));
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int k=1;k<=n;k++)
z.f[i][j]=((x.f[i][k]*y.f[k][j])%mo+z.f[i][j])%mo;
x=z;
}
data Power(data x,int y)
{
data res;
memset(res.f,0,sizeof(res.f));
for (int i=1;i<=n;i++)
res.f[i][i]=1;
for (;y;y>>=1)
{
if (y&1) mul(res,x);
mul(x,x);
}
return res;
}
data Sum(data x,int y)
{
data res;
memset(res.f,0,sizeof(res.f));
if (y==0) return res;
if (y==1) return x;
for (int i=1;i<=n;i++)
res.f[i][i]=1;
add(res,Power(x,y>>1));mul(res,Sum(x,y>>1));
if (y&1) add(res,Power(x,y));
return res;
}
int main()
{
read(n);read(m);
int x,y;
memset(a.f,0,sizeof(a.f));
for (int i=1;i<=m;i++)
read(x),read(y),a.f[x][y]=1,a.f[y][x]=1;
for (int i=1;i<=n;i++)
a.f[i][i]=1;
read(T);
b=a;
a=Power(a,T);
memset(g,0,sizeof(g));
memset(f,0,sizeof(f));
g[1]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
f[i]=((g[j]*a.f[i][j])%mo+f[i])%mo;
int ans=0;
for (int i=1;i<=n;i++)
ans=(ans+f[i])%mo;
b=Sum(b,T-1);
for(int i=1;i<=n;i++)
b.f[i][i]=(b.f[i][i]+1)%mo;
memset(g,0,sizeof(g));
memset(f,0,sizeof(f));
g[1]=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
f[i]=((g[j]*b.f[i][j])%mo+f[i])%mo;
for (int i=1;i<=n;i++)
ans=(ans+f[i])%mo;
print(ans),puts("");
return 0;
}