1. 程式人生 > >正睿2019省選十連測day3T3(笛卡爾樹,dp)

正睿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;
}