• [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的博客的,但是博主一直学不懂这玩意,只能被迫咕咕咕了。

  • 相关阅读:
    IP地址和MAC地址,以及arp攻击
    可爱的老婆
    win7 homebasic下,.net2008 连接oracle,提示错误OCIEnvCreate 失败,返回代码为 1,但错误消息文本不可用
    检讨
    数据库索引
    PB调用C#编写的DLL
    用c#开发可供PB调用的COM组件
    关于excel取消科学计数法的问题
    按键码对照
    JSONP学习资料
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/15950757.html
Copyright © 2020-2023  润新知