【ybtoj高效进阶6-1-3】【luogu P3758】行为方案 / 可乐

【ybtoj高效进阶6-1-3】【luogu P3758】行为方案 / 可乐

洛谷的数据加强版 :【luogu P5789】 可乐(数据加强版)

题目大意:

给你一个图,一开始你处于1号点,每一秒钟你可以干三件事:
(1):通过某些连边走往另一个点;
(2):呆在原地不动;
(3):在原地爆炸。
问过了 t 秒后你的行为方案总数是多少?

思路:

我们把呆在原地不动视为走了一个自环,原地爆炸看作走向了0号点。
考虑进行动态规划,
f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示从 i 点到 j 点走了 k 步后的方案。
根据乘法原理0,那么我们有 f [ i ] [ j ] [ k ] = ∑ f [ i ] [ x ] [ k − 1 ] ∗ f [ x ] [ j ] [ 1 ] f[i][j][k]=\sum f[i][x][k-1]*f[x][j][1] f[i][j][k]=f[i][x][k1]f[x][j][1] 其中 x 是 另一个可行点。
然后我们发现第三维只和 k-1和 1 有关,可以省略。
于是方程就变成了: f [ i ] [ j ] = ∑ f [ i ] [ x ] ∗ f [ x ] [ j ] f[i][j]=\sum f[i][x]*f[x][j] f[i][j]=f[i][x]f[x][j]
初始条件为: f [ i ] [ i ] = 1 , f [ i ] [ 0 ] = 1 f[i][i]=1,f[i][0]=1 f[i][i]=1f[i][0]=1
这就不会爆空间啦。
接着分析,我们发现这个方程一共要转移 t 次,再仔细看一下
t < = 1 0 9 t<=10^9 t<=109
暴力转移方程要等到天荒地老······ 于是我们再分析一下转移方程
有没有发现这个玩意很像矩阵乘法的运算法则?
那么转移 t 次就是将初始的矩阵乘方 t 次 (矩阵的两个下标是0 ~n * 0 ~ n),得到的结果就是答案。
矩阵快速幂,开!!!

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define r register
#define rep(i,x,y) for(r ll i=x;i<=y;++i)
#define per(i,x,y) for(r ll i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const ll V=110,p=2017;
ll n,m,x,y,ans,t;
struct node
{
	ll a[V][V];
	ll n,m;
}a;
node operator *(const node &a,const node &b)
{
	node c;
	c.n=a.n,c.m=b.m;
	memset(c.a,0,sizeof(c.a));
	rep(k,0,a.m)
	 rep(i,0,c.n)
	  rep(j,0,c.m)
	   c.a[i][j]=(c.a[i][j]+(a.a[i][k]*b.a[k][j])%p)%p;
	return c; 
}
node makeone(ll n)
{
	node x;
	x.n=x.m=n;
	rep(i,0,n)
	 rep(j,0,n)
	  if(i==j) x.a[i][j]=1;
	  else x.a[i][j]=0;
	return x;
}
node power(node x,ll y)
{
	node res=makeone(x.n);
	while(y)
	{
		if(y&1) res=res*x;
		x=x*x;
		y>>=1;
	}
	return res;
}
int main()
{
	scanf("%lld%lld",&n,&m);
	rep(i,1,m)
	{
		scanf("%lld%lld",&x,&y);
		a.a[x][y]=a.a[y][x]=1;
	}
	rep(i,0,n)
	{
		a.a[i][i]=1;
		a.a[i][0]=1;
	}
	a.m=a.n=n;
	scanf("%lld",&t);
	node res=power(a,t);
	rep(i,0,n) ans=(ans+res.a[1][i])%p;
	cout<<ans;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值