【TJOI2017】可乐

题目描述

加里敦星球的人们特别喜欢喝可乐。因而,他们的敌对星球研发出了一个可乐机器人,并且放在了加里敦星球的\(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++]='\0';
	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;
}

posted @ 2017-05-05 17:35  Xiejiadong  阅读(...)  评论(...编辑  收藏
levels of contents