目录
题面
得分情况
100pts: 10
95pts: 一些
75/80/85pts: 几个
60pts: 一些
50pts: 很多
45pts: 少量
35pts: 不少
20pts: 暴多
10pts: 一堆
0pts: 巨多
(上述数据及描述性语言来自NOI讲题人的ppt)
题解
20pts: n , B i ≤ 7 n,B_i\leq 7 n,Bi≤7
B i B_i Bi和 n n n都非常小,暴力模拟即可
35pts: B i ≤ 100 B_i\leq 100 Bi≤100
假设一个全局最高点 p p p,钦定 [ 1 , p − 1 ] [1,p-1] [1,p−1]内任意点高度小于等于 p p p的高度, [ p + 1 , n ] [p+1,n] [p+1,n]内任意点的高度小于 p p p的高度,显然, p p p将 [ 1 , n ] [1,n] [1,n]分割为 [ 1 , p − 1 ] [1,p-1] [1,p−1]和 [ p + 1 , n ] [p+1,n] [p+1,n]两个子问题。由此可以区间DP。
设 d p l , r , x dp_{l,r,x} dpl,r,x表示 [ l , r ] [l,r] [l,r]最大值恰好为x时的方案数,暴力枚举断点和最大值转移,容易得到:
d p l , r , x = ∑ m i d ∑ k ≤ x d p l , m i d − 1 , k ∑ k < x d p m i d + 1 , r , k dp_{l,r,x}=\sum_{mid}\sum_{k\leq x}dp_{l,mid-1,k}\sum_{k<x} dp_{mid+1,r,k} dpl,r,x=mid∑k≤x∑dpl,mid−1,kk<x∑dpmid+1,r,k
利用前缀和优化
参考代码:
#include<bits/stdc++.h>
#define ll long long
#define uit unsigned int
using namespace std;
const int N=301;
const ll M=1e9+7;
int n,A[N],B[N],dp[N][N][110];
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int MAX=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d%d",&A[i],&B[i]),MAX=max(MAX,B[i]);
for(int i=1;i<=n;i++){
for(int j=1;j<=MAX;j++){
if(j>=A[i]&&j<=B[i]) dp[i][i][j]++;
dp[i][i][j]=(dp[i][i][j]+dp[i][i][j-1])%M;
}
}
for(int len=2;len<=n;len++)
for(int i=1;i+len-1<=n;i++){
int j=i+len-1;
for(int k=i;k<=j;k++)
if(abs((k-i)-(j-k))<=2)
for(int w=A[k];w<=B[k];w++){
if(k==i) dp[i][j][w]=(dp[i][j][w]+dp[k+1][j][w-1])%M;
else if(k==j) dp[i][j][w]=(dp[i][j][w]+dp[i][k-1][w])%M;
else dp[i][j][w]=(dp[i][j][w]+1ll*dp[i][k-1][w]*dp[k+1][j][w-1]%M