正睿2019省选十连测day3T3(笛卡尔树,dp)

题面描述

有一个11到nn的排列p1,p2,p3,…,pn,你会对它进行若干轮操作,每一轮操作,你会保留序列中极大的数,也就是说对于每个数字,如果它比相邻的数字都大,那么会被保留下来。比如一个排列(3,2,5,1,4,6),经过一轮操作之后序列变成(3,5,6),第二轮操作之后序列变成(6),经过恰好2轮之后序列里只有一个元素。
请问有多少个长度为n的序列,经过恰好k次操作之后,序列里只有一个元素,由于答案很大,对一个素数P取模。

输入格式

第一行三个正整数n,k,P,其中P表示模数,保证P是素数,且108≤P≤109。

输出格式

输出一个数,表示答案。

样例输入

5 3 100000007

样例输出

4

数据规模

对于 10% 的数据,n≤10。
对于 30% 的数据,n≤20。
对于 60% 的数据,n≤100。
对于 100%1 的数据, 1≤k≤n≤103。


同时和位置和数字权值有关的问题。考虑转化到笛卡尔树上

以位置为二叉搜索树的键值,数字为堆的键值建立笛卡尔树
可以发现不考虑边界,记 d p [ i ] dp[i] 为一个点被消去的时间
可以得到 d p [ i ] = m a x ( d p [ l s ] , d p [ r s ] , m i n ( d p [ l s ] , d p [ r s ] ) + 1 ) dp[i]=max(dp[ls],dp[rs],min(dp[ls],dp[rs])+1)
边界需要特殊处理

把这个过程换成计数

f [ i ] [ j ] f[i][j] 表示 i i 个点, j j 轮消去的方案
g [ i ] [ j ] g[i][j] 表示 i i 个点, j j 轮消去,且当前根在边界上的方案
直接转移即可

#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back 
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
int rd()
{
	int num = 0;char c = getchar();bool flag = true;
	while(c < '0'||c > '9') {if(c == '-') flag = false;c = getchar();}
	while(c >= '0' && c <= '9') num = num*10+c-48,c = getchar();
	if(flag) return num;else return -num;
}
int n,m,p;
int f[1010][13],g[1010][13];
int c[1010][1010];
inline void chmax(int &a,int b){if(a<b)a=b;}
inline int max(int a,int b){return a>b?a:b;}
inline int mul(int a,int b){return 1ll*a*b%p;}
inline int calc(int a,int b){return (a+=b)>=p?a-=p:a;}
int main()
{
	n = rd();m = rd();p = rd();
    f[0][0] = g[0][0] = 1;
	c[0][0] = 1;
	rep(i,1,n){c[i][0] = c[i][i] = 1;rep(j,1,i-1) c[i][j] = calc(c[i-1][j-1],c[i-1][j]);}
	rep(i,1,n)
		rep(j,0,i-1)
		{//f[j][].f[k][]->f[i][]
		    int k = i-j-1;
			int t = c[i-1][j];
			rep(x,0,m) if(f[j][x]||g[j][x])
			    rep(y,0,m) if(f[k][y])
			        f[i][x==y?x+1:max(x,y)] = calc(f[i][x==y?x+1:max(x,y)],mul(f[j][x],mul(f[k][y],t))),
			        g[i][max(x,y+1)] = calc(g[i][max(x,y+1)],mul( g[j][x] , mul(f[k][y] , t) )); 
		}
	int ans = 0;
	rep(i,0,n-1)
	{
		int j = n-i-1;
	    rep(x,0,m) rep(y,0,m) if(x == m || y == m)
		    ans = calc(ans,mul( g[i][x] , mul(g[j][y],c[n-1][i]) ));
	}
	printf("%d\n",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/a1035719430/article/details/85066945