前言
由于从来没写过连续段DP导致完全没这么想,而且考试的时候脑子剧痛,现在也很痛,T3写出了一堆锅。
总之,哎,算了,我也不知道我在说什么,脑子好痛,可能没啥逻辑,我是sb。
题目
定义鹤
为一个队伍中比前后两个人都高的人,特别的,第一个人如果比第二个人高,或者最后一个人比倒数第二个人高,那么也是鹤
。
所以其他人是鸡
。
现在有 \(N\) 个身高互不相同的人,问有多少种排列方式使得恰有 \(M\) 只鹤
,答案对 \(P\) 取模。
\(2\le N\le 10^9;1\le M\le 10;1\le P\le 10^3.\)
讲解
观察发现每只鹤
两边都有身高单减的鸡
,大概是一个山峰的样子,我们把这些人看作一个连续段。
那么连续段DP大致有三种转移:增加连续段,合并连续段,连续段数量不变,但是某个连续段变长。
令 \(dp_{i,j,k}\) 表示从高到低考虑到了第 \(i\) 个人,目前有 \(j\) 只鹤
,连续段个数为 \(k\),转移在代码最下面,不过应该不难想。
\(i\) 很大所以用矩阵加速。
时间复杂度 \(O(m^6\log_2N)\)。
还有一种根据 \(P\) 很小因此找循环节的做法,这里不多赘述。
代码
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;
typedef long long LL;
const int MAXN = 100005;
int N,M,MOD;
LL Read()
{
LL x = 0,f = 1;char c = getchar();
while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
return x * f;
}
TT void Put1(T x)
{
if(x > 9) Put1(x/10);
putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
if(x < 0) putchar('-'),x = -x;
Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}
struct Matrix{
int n,m;
int a[100][100];
Matrix(){memset(a,0,sizeof(a));}
Matrix operator * (const Matrix &C)const{
Matrix ret; ret.n = n; ret.m = C.m;
for(int i = 0;i < n;++ i)
for(int k = 0;k < m;++ k)
if(a[i][k])
for(int j = 0;j < C.m;++ j)
ret.a[i][j] = (ret.a[i][j] + a[i][k] * C.a[k][j]) % MOD;
return ret;
}
}A,B;
int ID(int x,int y) {return (x-1)*M+y-1;}
void Ad(int &x,int y){(x += y) %= MOD;}
int main()
{
// freopen("queue.in","r",stdin);
// freopen("queue.out","w",stdout);
N = Read(); M = Read(); MOD = Read();
B.m = A.n = A.m = M*M; B.n = 1;
for(int j = 1;j <= M;++ j)
for(int k = 1;k <= M;++ k){
if(j < M && k < M) Ad(A.a[ID(j,k)][ID(j+1,k+1)],k+1);
if(k > 1) Ad(A.a[ID(j,k)][ID(j,k-1)],k-1);
Ad(A.a[ID(j,k)][ID(j,k)],k<<1);
}
B.a[0][ID(1,1)] = 1; --N;
while(N){
if(N & 1) B = B * A;
A = A * A; N >>= 1;
}
Put(B.a[0][ID(M,1)],'\n');
return 0;
}
/*
dp_{j,k}
(k+1) dp_{j+1,k+1} 单列
(k-1) dp_{j,k-1} 拼接
2k dp_{j,k} 延续
*/
后记
这里是和这道题完全无关的吐槽。
本来想写点插头DP的博客的,但是博主一直学不懂这玩意,只能被迫咕咕咕了。