1. 程式人生 > >洛谷4838 P哥破解密碼(dp+矩乘優化)

洛谷4838 P哥破解密碼(dp+矩乘優化)

題目連結

qwq一看資料範圍就知道這個題的做法

首先我們先考慮一個樸素的dp
我們令 f [ i ] [ 0 / 1

/ 2 ] f[i][0/1/2] 表示到了第 i i 位,有連續 0
, 1 , 2 0,1,2
A A 的方案數。

顯然,如果當前位置填 B

B
就相當於當前位置有0連續個A,那麼前 i 1 i-1 位填 0 , 1 , 2 0,1,2 都是合法的

如果當前位置填 A A
那麼就相當於當前位置有 1 , 2 1,2 個連續的A,分別能從前 i 1 i-1 位有 0 , 1 0,1 A A 轉移過來。

那麼轉移式子如下 f [ i ] [ 0 ] + = f [ i 1 ] [ 0 ] + f [ i 1 ] [ 1 ] + f [ i 1 ] [ 2 ] f [ i ] [ 1 ] = f [ i 1 ] [ 0 ] f [ i ] [ 2 ] = f [ i 1 ] [ 1 ] f[i][0]+=f[i-1][0]+f[i-1][1]+f[i-1][2] \\ f[i][1]=f[i-1][0] \\f[i][2]=f[i-1][1]

QWQ直接列舉的複雜度是 O ( n ) O(n) 的,無法接受。

我們考慮優化…其實看到 n n 1 n能從n-1 轉移,很顯然想到矩乘

構造如下矩陣

1 1 0 
1 0 1
1 0 0 

直接進行矩陣快速冪就好。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
using namespace std;
inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}
const int maxn = 10;
const int mod = 19260817;
struct Ju{
	int x,y;
	int a[maxn][maxn];
	Ju operator* (Ju b)
	{
		Ju ans;
		ans.x=x;
		ans.y=b.y;
		memset(ans.a,0,sizeof(ans.a));
		for (int i=1;i<=ans.x;i++)
		  for (int j=1;j<=ans.y;j++)
		    for (int k=1;k<=y;k++)
		      ans.a[i][j]=(ans.a[i][j]+a[i][k]*b.a[k][j]%mod)%mod;
		return ans;
	}
};
Ju qsm(Ju i,int j)
{
	Ju ans; 
	ans.x=i.x;
	ans.y=i.y;
	memset(ans.a,0,sizeof(ans.a));
	for (int p=1;p<=i.x;p++) ans.a[p][p]=1;
	while (j)
	{
		if(j&1) ans=ans*i;
		i=i*i;
		j>>=1;
	}
	return ans;
}
Ju a;
Ju b;
void init()
{
	memset(a.a,0,sizeof(a.a));
	a.x=1;
	a.y=3;
	a.a[1][1]=1;
	a.a[1][2]=1;
	a.a[1][3]=0;
	memset(b.a,0,sizeof(b.a));
	b.x=3;
	b.y=3;
	b.a[1][1]=1;
	b.a[1][2]=1;
	b.a[2][1]=1;
	b.a[2][3]=1;
	b.a[3][1]=1;
}
signed main()
{
  int m=read();
  while (m--)
  {
  	 int n=read();
  	 init();
  	 Ju tmp=qsm(b,n-1);
  	 a=a*tmp;
  	 cout<<(a.a[1][1]+a.a[1][2]+a.a[1][3])%mod<<endl;
  }
  return 0;
}