Liveddd's Blog

愛にできることはまだあるかい

SP3734 PERIODNI - Periodni

笛卡尔树上进行 DP。

HDU 1506 最大子矩形 也可以用笛卡尔树来做,具体思路就是对于高度建笛卡尔树,再一遍 dfs 即可计算。注意到这道题,直觉告诉我们也需要从高度最小的考虑。于是我们也考虑建出笛卡尔树进行 DP。

这样就变成一个树上背包计数,我们每一次递归子树的时候考虑 将当前列的高度去掉,只计算之上的,这样就不会影响到当前行。很明显有转移方程:$f_{x,i}\gets f_{x,i-j}\times f_{y,j}$。

再考虑将当前高度,就是计算在 $h_x\times size_x$ 的方格里选取 $k$ 个点的方案数。选 $k$ 行 $\binom{k}{h_x}$,选 $k$ 列 $\binom{k}{size_x}$,并且是无序的,所以方案数就是 $k! \cdot \binom{k}{h_x}\cdot \binom{k}{size_x}$。

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
const int N=5e2+10,M=1e7+10,mod=1e9+7;
template<class T>
inline void read(T &x)
{
x=0;bool f=0;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
if(f)x=-x;
}
template<class T,class ...T1>
inline void read(T &x,T1 &...x1)
{
read(x),read(x1...);
}
struct Node
{
int l,r;
int val;
}tr[N];
int n,k;
int top,sta[N];
ll fac[M];
int si[N];
ll f[N][N];
void init()
{
fac[1]=1;
for(int i=2;i<M;i++)
fac[i]=fac[i-1]*i%mod;
}
ll inv(ll x)
{
if(x<=1)return 1;
return (mod-mod/x)*inv(mod%x)%mod;
}
ll C(int n,int m)
{
if(m>n)return 0;
return fac[n]*inv(fac[m])%mod*inv(fac[n-m])%mod;
}
void dfs(int x,int res)
{
f[x][0]=si[x]=1;
if(tr[x].l)
{
int y=tr[x].l;
dfs(y,tr[x].val);
si[x]+=si[y];
for(int i=min(si[x],k);~i;i--)
{
for(int j=1;j<=min(i,si[y]);j++)
f[x][i]=(f[x][i]+f[x][i-j]*f[y][j])%mod;
}
}
if(tr[x].r)
{
int y=tr[x].r;
dfs(y,tr[x].val);
si[x]+=si[y];
for(int i=min(si[x],k);~i;i--)
{
for(int j=1;j<=min(i,si[y]);j++)
f[x][i]=(f[x][i]+f[x][i-j]*f[y][j]%mod)%mod;
}
}
for(int i=min(si[x],k);~i;i--)
for(int j=1;j<=min(i,tr[x].val-res);j++)
f[x][i]=(f[x][i]+f[x][i-j]*fac[j]%mod*C(tr[x].val-res,j)%mod*C(si[x]-(i-j),j)%mod)%mod;
}
int main()
{
init();
read(n,k);
for(int i=1;i<=n;i++)
read(tr[i].val);
for(int i=1;i<=n;i++)
{
int p=top;
while(p&&tr[sta[p]].val>tr[i].val)
p--;
if(p)tr[sta[p]].r=i;
if(p<top)tr[i].l=sta[p+1];
sta[++p]=i;
top=p;
}
dfs(sta[1],0);
printf("%lld\n",f[sta[1]][k]);
return 0;
}