SP3734 PERIODNI - Periodni(笛卡尔树)
据说是一道笛卡尔树经典例题,本蒟蒻拿来加强一下理解的(只简单地总结下为什么用笛卡尔树,以及笛卡尔树的性质,之后树形DP的细节就不讲了),所以想看题解的或许可以转到别的大佬那边去了
链接:https://www.luogu.com.cn/problem/SP3734
其实样例已经说的已经很清楚了,两个数会不会相互影响,取决于中间有没有一个高度比它们都小的矩形把它们隔开,如果我们把矩形高度视为数列,而一个区间最小的矩形高度就是瓶颈值,即是关键的,受这个瓶颈值控制,于是对于这种关键取决于最小值之类的题,我们常常用堆结构来限制,如kruskal重构树,又如本题的笛卡尔树.所以我们以数列编号为BST关键字,矩阵高度为堆关键字,即可把一个不规则图形拆成若干个规则的矩形,
从而解决这道题
/*SP3734 PERIODNI - Periodni*/ #include<cstdio> #include<iostream> #include<cstring> #include<algorithm> using namespace std; #define ll long long const int maxn = 1e3 + 10; int lc[maxn],rc[maxn]; int dp[maxn][maxn]; int f[maxn]; int h[maxn]; int st[maxn],top; int siz[maxn]; bool nrt[maxn]; const int mod = 1e9 + 7; const int maxsize = 1e6 + 10; int fa[maxn]; int jc[maxsize],invjc[maxsize]; int n,cnt; int C(int x,int y){ return 1ll * jc[x] * invjc[y] % mod * invjc[x-y] % mod; } int qpow(int x,int y){ int ans = 1; while(y){ if(y & 1) ans = 1ll * ans * x % mod; x = 1ll * x * x % mod; y >>= 1; } return ans; } int read(){ char c = getchar(); int x = 0; while(c < '0' || c > '9') c = getchar(); while(c >= '0' && c <= '9') x = x * 10 + c - 48,c = getchar(); return x; } int Add(int x,int y){ x += y; return (x >= mod)?x - mod:x; } void treedp(int x){ siz[x] = 1; int H = h[x] - h[fa[x]]; dp[x][0] = 1; // cout<<x<<endl; if(lc[x]){ fa[lc[x]] = x,treedp(lc[x]); memcpy(f,dp[x],sizeof(f)); memset(dp[x],0,sizeof(dp[x])); for(int i = 0; i <= siz[x]; ++i){ for(int j = 0; j <= siz[lc[x]]; ++j) dp[x][i + j] = Add(dp[x][i + j],1ll * f[i] * dp[lc[x]][j] % mod); } siz[x] += siz[lc[x]]; } if(rc[x]){ fa[rc[x]] = x,treedp(rc[x]); memcpy(f,dp[x],sizeof(f)); memset(dp[x],0,sizeof(dp[x])); for(int i = 0; i <= siz[x]; ++i){ for(int j = 0; j <= siz[rc[x]]; ++j) dp[x][i + j] = Add(dp[x][i + j],1ll * f[i] * dp[rc[x]][j] % mod); } siz[x] += siz[rc[x]]; } memcpy(f,dp[x],sizeof(f)); memset(dp[x],0,sizeof(dp[x])); for(int i = 0; i <= siz[x]; ++i){ if(i > H) break; for(int j = 0; j <= siz[x] - i; ++j){ dp[x][i+j] = Add(dp[x][i+j],1ll * f[j] * C(siz[x] - j,i) % mod * jc[i] % mod * C(H,i) % mod); } } } int main(){ jc[0] = invjc[0] = 1; int mx = 1e6; for(int i = 1; i <= mx; ++i) jc[i] = 1ll * jc[i-1] * i % mod; invjc[mx] = qpow(jc[mx],mod-2); for(int i = mx - 1; i >= 0; --i) invjc[i] = 1ll * invjc[i+1] * (i + 1) % mod; n = read(),cnt = read(); for(int i = 1; i <= n; ++i) h[i] = read(); for(int i = 1; i <= n; ++i){ int k = top; while(h[st[k]] > h[i]) k--; if(k) rc[st[k]] = i; if(k != top) lc[i] = st[k+1]; st[++k] = i; top = k; } for(int i = 1; i <= n; ++i) nrt[lc[i]] = nrt[rc[i]] = true; int rt = 0; for(int i = 1; i <= n; ++i){ if(!nrt[i]){ rt = i; break; } } treedp(rt); // cout<<dp[3][0]<<endl; printf("%d\n",dp[rt][cnt]); return 0; }