Codeforces 821E Okabe and El Psy Kongroo 递推+矩阵幂

点击打开链接

题意:n条直线(a[i],b[i],c[i]) 表示在x=a[i]~b[i]内 运动的高度0<=y<=c[i],
a[i]=b[i-1],a[1]=0,a[n]<=k<=b[n],一个人有三个运动方向(x+1,y+1),(x+1,y),(x+1,y-1) 
n<=100,c[i]<=15, a[i],b[i],k<=1e18 问从(0,0)->(k,0)的方法数?


设f[x][y] 从(0,0)到点(x,y)的方法数,
f[x][y]=f[x-1][y]+f[x-1][y-1]+f[x-1][y+1] 注意转移状态时要合法,x>=0&&0<=y<=c[i]

x很大并且x每次都只加1 明显用矩阵幂来优化 s(i,j)为纵坐标i->j的总方法数
构造h^x(i,j) 表示经过x步后 纵坐标从i变化到j的步数

根据每段的c[i],算出h^1,矩阵幂得到h^m,在更新s即可.O(n*h^3*logw)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; 
const int N=20;
const int M=2e2;
const ll mod=1e9+7;
ll s[N][N],h[N][N],t[N][N];//¾­¹ýx²½ºó ×Ý×ø±ê´Ói±äµ½jµÄ·½·¨Êýh[i][j]
ll n,K,a[M],b[M],c[M];
void mul(ll a[][N],ll b[][N],ll c[][N])
{
	ll t[N][N];
	for(int i=1;i<=16;i++)
	{
		for(int j=1;j<=16;j++)
		{
			t[i][j]=0;
			for(int k=1;k<=16;k++)
			{
				t[i][j]+=(a[i][k]*b[k][j])%mod;
				t[i][j]%=mod;
			}
		}
	}
	for(int i=1;i<=16;i++)
		for(int j=1;j<=16;j++)
			c[i][j]=t[i][j]%mod;	
}
void powmod(ll n)
{
	memset(t,0,sizeof(t));
	for(int i=1;i<=16;i++)
		t[i][i]=1;
	while(n)
	{
		if(n&1)
			mul(h,t,t);
		n>>=1;
		mul(h,h,h);
	}
}
int main()
{
	while(cin>>n>>K)
	{
		memset(s,0,sizeof(s));
		for(int i=1;i<=n;i++)
			scanf("%I64d%I64d%I64d",&a[i],&b[i],&c[i]),c[i]++;
		memset(h,0,sizeof(h));
		for(int i=1;i<=16;i++)//h^0
			s[i][i]=h[i][i]=1;
		for(int x=1;x<=n;x++)
		{
			memset(h,0,sizeof(h));
			for(int i=1;i<=c[x];i++)
			{
				for(int j=1;j<=c[x];j++)
				{
					if(abs(i-j)<=1)
						h[i][j]=1;
				}
			}
			ll m=b[x]-a[x];
			if(x==n)
				m=K;
			powmod(m);
			K-=(b[x]-a[x]);
			mul(t,s,s);
		}
		cout<<s[1][1]<<endl;
	}
	return 0;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值