LGP6453 [COCI 2008.5 R4] PERIODNI 学习笔记

LGP6453 [COCI 2008.5 R4] PERIODNI 学习笔记

Luogu Link

题意简述

给定一个 \(n\) 列组成的表格。第 \(i\) 列高度为 \(a_i\),且底部都是对齐的。需要在里面放入 \(k\) 个元素,并保证没有任意两个元素处在连续的相同行或相同列中。

求总方案数,对 \(10^9+7\) 取模。

\(n,k\le 500\)\(a_i\le 10^6\)

做法解析

类似于LGP11261地,考虑极值分治。显然你需要建小根笛卡尔树,这样答案才像是可以合并的。

因为按小根建笛卡尔树之后,设当前我们考虑区间的最小值下标为 \(u\),我们要求递归子问题中填元素的高度高于 \(a_u\),这样的话合并答案就没有后效性,剩下的行的填法不会受到子问题影响。

具体来说,我们设 \(h_u=a_u-a_{fa_u}\)。在 \(u\) 递归完子问题后,我们要加上的考虑的矩形的长度即为笛卡尔树上 \(u\) 管的区间 \(siz_u\),高度即为 \(h_u\)

另外我们设 \(f_{u,i}\) 表示在 \(u\) 笛卡尔树管的区间内总共填 \(i\) 个数的方案数。这样对于递归边界处,我们有 \(f_{u,0}=1,f_{u,1}=h_u\)

怎么合并答案呢?我们要把 \(f_{u,0}\)\(f_{u,k}\) 中的每一个都转移一下。对于 \(f_{u,k}\),我们自然想到枚举左儿子填了 \(i\) 个,右儿子填了 \(j\) 个,那么 \(u\) 自己就要填剩下的 \(k-i-j\) 个。因此有:\(f_{u,k}=f_{ls,i}\times f_{ls,j}\times Arra(siz_u-i-j,k-i-j)\times \dbinom{h_u}{k-i-j}\)。其中 \(Arra()\) 表示排列。后面那一串就是说对于 \(u\) 管的矩形,要从 \(siz_u-i-j\) 列中选 \(k-i-j\) 列,\(h_u\) 行中选 \(k-i-j\) 行来确定要填的 \(k-i-j\) 个元素,行和列有对应关系,元素之间不区分。此时总复杂度 \(O(nk^3)\)

你观察到这个式子里面不存在形如与 \(k,i\) 相关而不与 \(j\) 相关的项。这启示你先把所有 \(i,j\) 对拎出来预处理它们的答案,再枚举 \(i+j\)

所以你先处理出 \(g_{u,p}=\sum_{i+j=p}f_{ls,i}\times f_{rs,j}\)。然后时间复杂度就优化到 \(O(nk^2)\) 了。

代码实现

#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
using namespace omodint;
using mint=m107;
const int MaxN=5e2+5,MaxK=5e2+5,MaxV=1e6+5;
using namespace omathe;
int N,K,A[MaxN],siz[MaxN],H[MaxN],V=1e6;
int stk[MaxN],ktp,ls[MaxN],rs[MaxN];
mint f[MaxN][MaxK],g[MaxN][MaxK];
void solve(int cl,int cr,int u){
    siz[u]=cr-cl+1;
    if(cl==cr){f[u][0]=1,f[u][1]=H[u];return;}
    if(ls[u])H[ls[u]]-=A[u],solve(cl,u-1,ls[u]);
    if(rs[u])H[rs[u]]-=A[u],solve(u+1,cr,rs[u]);
    for(int p=0;p<=min(siz[u]-1,K);p++){
        for(int i=0,j=p;i<=p;i++,j--){
            g[u][p]+=f[ls[u]][i]*f[rs[u]][j];
        }
    }
    for(int k=0;k<=min(siz[u],K);k++){
        for(int p=max(0,k-H[u]);p<=k;p++){
            f[u][k]+=g[u][p]*(Arra(siz[u]-p,k-p)*Comb(H[u],k-p));
        }
    }
}
int main(){
    readis(N,K),premwork(V);
    for(int i=1;i<=N;i++)readi(A[i]),H[i]=A[i];
    for(int i=1;i<=N;i++){
        while(ktp&&A[stk[ktp]]>=A[i])ls[i]=stk[ktp--];
        rs[stk[ktp]]=i,stk[++ktp]=i;
    }
    f[0][0]=1;
    solve(1,N,stk[1]);writi(miti(f[stk[1]][K]));
    return 0;
}
posted @ 2025-06-18 14:57  矞龙OrinLoong  阅读(3)  评论(0)    收藏  举报