[SPOJ][BZOJ2616]PERIODNI

\[\color{red}{\text{校长者,真神人也,左马桶,右永神,会执利笔破邪炁,何人当之?}} \\ \begin{array}{|} \hline \color{pink}{\text{The principal is really a god}} \\ \color{pink}{\text{with a closestool on the left and Yongshen on the right}} \\ \color{pink}{\text{holding a sharp pen to pierce the truth}} \\ \color{pink}{\text{Who can resist him? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{green}{\text{校長は本当に神であり、左側にトイレ、右側にヨンシェンがあり}} \\ \color{green}{\text{鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{lightblue}{\text{Le principal est vraiment un dieu}} \\ \color{lightblue}{\text{avec des toilettes à gauche et Yongshen à droite}} \\ \color{lightblue}{\text{tenant un stylo pointu pour percer la vérité}} \\ \color{lightblue}{\text{Qui peut lui résister ? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{purple}{\text{Der Direktor ist wirklich ein Gott}} \\ \color{purple}{\text{mit einer Toilette links und Yongshen rechts}} \\ \color{purple}{\text{der einen spitzen Stift hält}} \\ \color{purple}{\text{um die Wahrheit zu durchdringen.}} \\ \color{purple}{\text{Wer kann ihm widerstehen? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{cyan}{\text{Principalis deus est, Yongshen a dextris cum latrina}} \\ \color{cyan}{\text{acuto stylo ad perforandum veritatem: quis resistet ei? }} \\ \hline \end{array} \\ \color{red}{\text{对曰:“无人,狗欲当之,还请赐教!”}} \\ \newcommand\bra[1]{\left({#1}\right)} \newcommand\Bra[1]{\left\{{#1}\right\}} \newcommand\dx[0]{\text{dx}} \newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}} \newcommand\down[2]{{#1}^{\underline{#2}}} \newcommand\ddiv[2]{\left\lfloor\frac{#1}{#2}\right\rfloor} \newcommand\udiv[2]{\left\lceil\frac{#1}{#2}\right\rceil} \newcommand\lcm[0]{\operatorname{lcm}} \newcommand\set[1]{\left\{{#1}\right\}} \]

壹、题目描述 ¶

传送门 to DarkBZOJ.

贰、题解 ¶

二维直方图,可以考虑使用笛卡尔树。平凡地,按照 \((i,h_i)\) 建立笛卡尔树。这样有什么好处呢?显然一个点的左右儿子可以任意放车而不会影响彼此。

接下来显然是树 \(\rm DP\) 的过程,考虑设 \(dp(i,j)\) 表示在 \(i\) 的子树中放 \(j\) 个车的方案数,接下来我们考察,如何从左右儿子到当前点 \(u\) ?我们不妨设 \(l,r\) 表示左右儿子,\(H\) 表示 \(h_u-h_{fa(u)}\)\(W\) 表示 \(siz_u\),即扩展出的矩形宽度。

先考虑左右儿子的任意匹配,记

\[tmp(j)=\sum_{x=0}^jdp(ls,x)\times dp(rs,j-x) \]

那么,\(tmp(j)\) 即表示在儿子所管辖的范围中放了 \(j\) 个车的方案数,接下来考虑在 \(u\) 所管辖的 \(H\times W\) 的矩阵中放置的方案数,显然有

\[dp(i,j)=\sum_{x=0}^j tmp(j-x)\times {W-j+x\choose x}\times\down{H}{x} \]

总复杂度 \(\mathcal O(NK^2)\),不知道为什么本机测试 \(\rm TLE\) 了......

叁、参考代码 ¶

# include <cstdio>
# include <algorithm>
# include <cstring>
using namespace std;

# define NDEBUG
# include <cassert>

namespace Elaina {

# define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
# define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
# define fi first
# define se second
# define mp(a, b) make_pair(a, b)
# define Endl putchar('\n')
# define mmset(a, b) memset(a, b, sizeof (a))
# define mmcpy(a, b) memcpy(a, b, sizeof (a))

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <int, int> pii;
typedef pair <ll, ll> pll;

template <class T> inline T fab(T x) { return x<0? -x: x; }
template <class T> inline void getmin(T& x, const T rhs) { x=min(x, rhs); }
template <class T> inline void getmax(T& x, const T rhs) { x=max(x, rhs); }
template <class T> inline T readin(T x) {
    x=0; int f=0; char c;
    while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
    for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
    return f? -x: x;
}

template <class T> inline void writc(T x, char s='\n') {
    static int fwri_sta[1005], fwri_ed=0;
    if(x<0) putchar('-'), x=-x;
    do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
    while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
    putchar(s);
}

} using namespace Elaina;

const int mod=1e9+7;
const int maxn=500;
const int maxk=500;
const int maxh=1000000;

inline int qkpow(int a, int n) {
    int ret=1;
    for(; n>0; n>>=1, a=1ll*a*a%mod)
        if(n&1) ret=1ll*ret*a%mod;
    return ret;
}

int finv[maxh+5], fac[maxh+5];
inline void prelude() {
    finv[0]=fac[0]=1;
    rep(i, 1, maxh) fac[i]=1ll*fac[i-1]*i%mod;
    finv[maxh]=qkpow(fac[maxh], mod-2);
    for(int i=maxh-1; i; --i) finv[i]=1ll*finv[i+1]*(i+1)%mod;
}
inline int down(int n, int x) {
    if(n==0 && x==0) return 1;
    if(x>n) return 0;
    return 1ll*fac[n]*finv[n-x]%mod;
}
inline int C(int n, int m) {
    if(n<m || n<0 || m<0) return 0;
    return 1ll*down(n, m)*finv[m]%mod;
}

int n, k;
int h[maxn+5];

inline void input() {
    n=readin(1), k=readin(1);
    rep(i, 1, n) h[i]=readin(1);
}

int ls[maxn+5], rs[maxn+5], rt;
int stk[maxn+5], ed;
inline void build() {
    rep(i, 1, n) {
        while(ed && h[stk[ed]]>h[i]) ls[i]=stk[ed--];
        if(!ed) rt=i;
        else rs[stk[ed]]=i;
        stk[++ed]=i;
    }
}

int fa[maxn+5], siz[maxn+5];
void dfs1(int u, int par) {
    fa[u]=par;
    if(ls[u]) dfs1(ls[u], u);
    if(rs[u]) dfs1(rs[u], u);
    siz[u]=siz[ls[u]]+siz[rs[u]]+1;
}

int dp[maxn+5][maxk+5], tmp[maxk+5];
void dfs2(int u) {
    if(ls[u]) dfs2(ls[u]);
    if(rs[u]) dfs2(rs[u]);
    int H=h[u]-h[fa[u]], l=ls[u], r=rs[u];
    memset(tmp, 0, k+1<<2);
    rep(j, 0, k) rep(x, 0, j)
        tmp[j]=(tmp[j]+1ll*dp[l][x]*dp[r][j-x]%mod)%mod;
    rep(j, 0, k) rep(x, 0, j) {
        dp[u][j]=(dp[u][j]+1ll*tmp[j-x]*C(siz[u]-j+x, x)%mod*down(H, x)%mod)%mod;
    } dp[u][0]=1;
}

signed main() {
    prelude();
    input();
    build();
    dfs1(rt, 0);
    dp[0][0]=1; // special !
    dfs2(rt);
    writc(dp[rt][k]);
    return 0;
}

肆、关键 の 地方 ¶

遇到直方图的时候,可以考虑使用笛卡尔树(或者其思想),笛卡尔树的本质,就是将直方图划分成多个子矩阵解决,这在很多情况下是十分便于处理的。

posted @ 2021-09-01 21:27  Arextre  阅读(44)  评论(0编辑  收藏  举报