[20220301联考] 鹤立鸡群

前言

由于从来没写过连续段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的博客的,但是博主一直学不懂这玩意,只能被迫咕咕咕了。

posted @ 2022-03-01 16:02  皮皮刘  阅读(29)  评论(0编辑  收藏  举报