题解 暴雨
神仙题,想DP但完全不知如何入手
难点在于对状态的划分
到了某一列可行的积水体积与接下来列的最高高度有关,是向后关联的
但发现若已知前面的最大值,则积水高度肯定不能超过这个最大值,所以后面的高度只要 \(\geqslant\) 这个最大值就能产生贡献
于是令 \(f[i][j][p][0/1]\) 表示表示前 \(i\)列,最大值是 \(j\),且要求在后面存在一个高度至少为 \(j\) 的土地,已经铲平了 \(p\) 块,当前积水体积为奇数/偶数的方案数
因为 \(p\) 的取值范围只有 \(k+1\),所以合法的 \(j\) 的数量也只有 \(k+1\) 个
然后令一个 \(g[i][j][p][0/1]\) 从右往左再做一遍
合并的话枚举取到列高最大值的列进行合并
- 当发现DP前后互相关联的时候尝试对前后分别DP,然后找一个能让前后关联性断开的地方合并(本题中是最高的列能让前后积水高度失去关联)
- 当在枚举最大/小值合并DP值的时候特别注意最大/小值可能有多个的情况,一种常用的方法是一边取等,一边不取等
其原理是只在最左/右边的那个最大值处统计答案 - 当DP状态需要用map记录时,一个卡常方法是先将可能取到的值离散化,再建立一个追溯数组,然后就可以像map一样用了
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 25010
#define ll long long
#define fir first
#define sec second
// #define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k;
int h[N];
const ll mod=1e9+7;
inline void md(int& a, int b) {a+=b; a=a>=mod?a-mod:a;}
namespace force{
int top, ans;
struct que{int h, pos;}q[N];
void solve() {
int lim=1<<n;
for (int s=0; s<lim; ++s) {
int s2=s, cnt=0;
if (s) do {++cnt; s2&=s2-1;} while (s2) ;
if (cnt!=n-k) continue;
top=0;
int sum=0;
for (int i=0; i<n; ++i) if (s&(1<<i)) {
int lst=0;
for (int j=top; j; --j) {
sum+=max((min(q[j].h, h[i+1])-lst)*((i+1)-q[j].pos-1), 0);
lst=q[j].h;
}
while (top && q[top].h<=h[i+1]) --top;
q[++top].h=h[i+1]; q[top].pos=i+1;
}
if (!(sum&1)) {
++ans;
// cout<<"add: "<<bitset<10>(s)<<' '<<sum<<endl;
}
}
printf("%lld\n", ans);
exit(0);
}
}
namespace task{
int siz1[N], siz2[N], bkp1[N][27], bkp2[N][27];
int f[N][27][26][2], g[N][27][26][2]; ll ans;
unordered_map<int, int> mp1[N], mp2[N];
set<int> s1, s2;
void solve() {
++siz1[0]; mp1[0][0]=siz1[0]; f[0][mp1[0][0]][0][0]=1; bkp1[0][siz1[0]]=0;
s1.insert(0); ++siz1[1]; mp1[1][0]=siz1[1]; bkp1[1][siz1[1]]=0;
for (int i=1; i<=n; ++i) {
s1.insert(h[i]);
auto it=s1.rbegin();
for (int j=1; j<=k+1&&it!=s1.rend(); ++j,++it) {
// cout<<"j: "<<j<<' '<<*it<<endl;
mp1[i][*it]=++siz1[i];
bkp1[i][siz1[i]]=*it;
}
}
f[1][mp1[1][h[1]]][0][0]=1; f[1][mp1[1][0]][1][0]=1;
for (int i=1; i<n; ++i) {
for (int j=1; j<=siz1[i]; ++j) {
int dlt=max(bkp1[i][j], h[i+1])-h[i+1], tem;
if (mp1[i+1].find(max(bkp1[i][j], h[i+1]))!=mp1[i+1].end()) {
tem=mp1[i+1][max(bkp1[i][j], h[i+1])];
for (int p=0; p<=k; ++p) {
md(f[i+1][tem][p][dlt&1], f[i][j][p][0]);
md(f[i+1][tem][p][(dlt+1)&1], f[i][j][p][1]);
}
}
if (mp1[i+1].find(bkp1[i][j])!=mp1[i+1].end()) {
tem=mp1[i+1][bkp1[i][j]];
for (int p=0; p<k; ++p) {
md(f[i+1][tem][p+1][bkp1[i][j]&1], f[i][j][p][0]);
md(f[i+1][tem][p+1][(bkp1[i][j]+1)&1], f[i][j][p][1]);
}
}
}
}
#if 0
// cout<<"mp4: "; for (auto it:mp[4]) cout<<it.fir<<' '; cout<<endl;
cout<<"---f---"<<endl;
for (int i=1; i<=n; ++i) {
for (auto j:mp1[i]) {
for (int p=0; p<=k; ++p) {
if (f[i][j.sec][p][0]) printf("f[%lld][%lld][%lld][%lld]=%lld\n", i, j.fir, p, 0ll, f[i][j.sec][p][0]);
if (f[i][j.sec][p][1]) printf("f[%lld][%lld][%lld][%lld]=%lld\n", i, j.fir, p, 1ll, f[i][j.sec][p][1]);
}
}
}
#endif
++siz2[n+1]; mp2[n+1][0]=siz2[n+1]; g[n+1][mp2[n+1][0]][0][0]=1; bkp2[n+1][siz2[n+1]]=0;
s2.insert(0); ++siz2[n]; mp2[n][0]=siz2[n]; bkp2[n][siz2[n]]=0;
for (int i=n; i; --i) {
s2.insert(h[i]);
auto it=s2.rbegin();
for (int j=1; j<=k+1&&it!=s2.rend(); ++j,++it) {
// cout<<"j: "<<j<<' '<<*it<<endl;
mp2[i][*it]=++siz2[i];
bkp2[i][siz2[i]]=*it;
}
}
g[n][mp2[n][h[n]]][0][0]=1; g[n][mp2[n][0]][1][0]=1;
for (int i=n; i>1; --i) {
for (int j=1; j<=siz2[i]; ++j) {
int dlt=max(bkp2[i][j], h[i-1])-h[i-1], tem;
if (mp2[i-1].find(max(bkp2[i][j], h[i-1]))!=mp2[i-1].end()) {
tem=mp2[i-1][max(bkp2[i][j], h[i-1])];
for (int p=0; p<=k; ++p) {
md(g[i-1][tem][p][dlt&1], g[i][j][p][0]);
md(g[i-1][tem][p][(dlt+1)&1], g[i][j][p][1]);
}
}
if (mp2[i-1].find(bkp2[i][j])!=mp2[i-1].end()) {
tem=mp2[i-1][bkp2[i][j]];
for (int p=0; p<k; ++p) {
md(g[i-1][tem][p+1][bkp2[i][j]&1], g[i][j][p][0]);
md(g[i-1][tem][p+1][(bkp2[i][j]+1)&1], g[i][j][p][1]);
}
}
}
}
#if 0
cout<<"---g---"<<endl;
for (int i=n; i; --i) {
for (auto j:mp2[i]) {
for (int p=0; p<=k; ++p) {
if (g[i][j.sec][p][0]) printf("g[%lld][%lld][%lld][%lld]=%lld\n", i, j.fir, p, 0ll, g[i][j.sec][p][0]);
if (g[i][j.sec][p][1]) printf("g[%lld][%lld][%lld][%lld]=%lld\n", i, j.fir, p, 1ll, g[i][j.sec][p][1]);
}
}
}
#endif
for (int i=1; i<=n; ++i)
for (int t1=1; t1<=siz1[i-1]; ++t1) if (bkp1[i-1][t1]<h[i])
for (int t2=1; t2<=siz2[i+1]; ++t2) if (bkp2[i+1][t2]<=h[i])
for (int p=0; p<=k; ++p) {
// cout<<"t: "<<t1<<' '<<t2<<endl;
// cout<<"merge0: "<<i<<' '<<it1.fir<<' '<<it2.fir<<' '<<f[i][it1.sec][p][0]<<' '<<g[i+1][it2.sec][k-p][0]<<endl;
// cout<<"merge1: "<<i<<' '<<it1.fir<<' '<<it2.fir<<' '<<f[i][it1.sec][p][1]<<' '<<g[i+1][it2.sec][k-p][1]<<endl;
ans=(ans+1ll*f[i-1][t1][p][0]*g[i+1][t2][k-p][0])%mod;
ans=(ans+1ll*f[i-1][t1][p][1]*g[i+1][t2][k-p][1])%mod;
}
printf("%lld\n", ans);
exit(0);
}
}
signed main()
{
freopen("rain.in", "r", stdin);
freopen("rain.out", "w", stdout);
n=read(); k=read();
for (int i=1; i<=n; ++i) h[i]=read();
// force::solve();
task::solve();
return 0;
}