题解 暴雨
Description
有 \(n\) 座山,可以铲平 \(k\) 座山,问使得积水为偶数的方案数是多少,模 \(10^9+7\) 。
\(n\le 2.5\times 10^4,k\le 25\)
Solution
很sb的一个题目,但是我傻了。
可以先考虑如何计算最后的积水体积。我们可以设 \(pre_i\) 表示前面 \(i\) 座山的最高值,\(suf_i\) 表示后面 \(i\) 座山的最高值,那么第 \(i\) 座山会产生的贡献就是 \(\min(pre_i,suf_i)-h_i\)。
我们就可以考虑枚举最后的最大值在哪里,假设在 pos,那么 pos 之前的山的积水就一定只与前面的最大值有关系,pos 之后的山的积水就一定只与后面的最大值有关系。
所以我们就可以 dp 了,可以设 \(f_{i,j,k,0/1}\) 表示前面 \(i\) 座山最大值为 \(j\) ,已经铲了 \(k\) 座,积水为 \(0/1\) 的方案数,从后往前的同理。
注意到最大值很大,但是 \(k\le 25\),也就是说对于每一个 \(i\) ,真正会贡献的 \(j\) 不会超过 \(k+1\) 个,所以我们就可以在 \(\Theta(nk^2)\) 计算出答案。
不知道为什么我的代码只能在本地通过测试,似乎有 UB ??
Code
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define mod 1000000007
#define MAXN 30005
#define MAXM 30
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}
int n,K,h[MAXN],p[MAXN],c1[MAXN],c2[MAXN],s1[MAXN][MAXM],s2[MAXN][MAXM],f[MAXN][MAXM][MAXM][2],g[MAXN][MAXM][MAXM][2];
int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
void Add (int &a,int b){a = add (a,b);}
void Sub (int &a,int b){a = dec (a,b);}
void makeit1 (int i,int j){
int pos = 0;
for (Int k = 0;k <= c1[i];++ k)
if (s1[i - 1][j] == s1[i][k]) pos = k;
for (Int k = 0;k <= K;++ k) for (Int w = 0;w < 2;++ w){
if (k < K) Add (f[i][pos][k + 1][(w + s1[i][pos]) & 1],f[i - 1][j][k][w]);
if (h[i] > s1[i][pos]) Add (f[i][p[i]][k][w],f[i - 1][j][k][w]);
else Add (f[i][pos][k][(w + s1[i][pos] - h[i]) & 1],f[i - 1][j][k][w]);
}
}
void makeit2 (int i,int j){
int pos = 0;
for (Int k = 0;k <= c2[i];++ k)
if (s2[i + 1][j] == s2[i][k]) pos = k;
for (Int k = 0;k <= K;++ k) for (Int w = 0;w < 2;++ w){
if (k < K) Add (g[i][pos][k + 1][(w + s2[i][pos]) & 1],g[i + 1][j][k][w]);
if (h[i] > s2[i][pos]) Add (g[i][p[i]][k][w],g[i + 1][j][k][w]);
else Add (g[i][pos][k][(w + s2[i][pos] - h[i]) & 1],g[i + 1][j][k][w]);
}
}
#define pii pair<int,int>
signed main(){
freopen ("rain.in","r",stdin);
freopen ("rain.out","w",stdout);
read (n,K);
for (Int i = 1;i <= n;++ i) read (h[i]);
set <pii> S;
for (Int i = 1;i <= n;++ i){
S.insert ({h[i],i});
while (S.size() > K + 1) S.erase (S.begin());
for (auto it = S.begin();it != S.end();++ it){
s1[i][++ c1[i]] = (it -> first);
if (s1[i][c1[i]] == h[i]) p[i] = c1[i];
}
}
f[0][0][0][0] = 1;
for (Int i = 1;i <= n;++ i)
for (Int j = 0;j <= c1[i - 1];++ j)
makeit1 (i,j);
S.clear ();
for (Int i = n;i >= 1;-- i){
S.insert ({h[i],-i});
while (S.size() > K + 1) S.erase (S.begin());
for (auto it = S.begin();it != S.end();++ it){
s2[i][++ c2[i]] = (it -> first);
if (s2[i][c2[i]] == h[i]) p[i] = c2[i];
}
}
g[n + 1][0][0][0] = 1;
for (Int i = n;i >= 1;-- i)
for (Int j = 0;j <= c2[i + 1];++ j)
makeit2 (i,j);
int res = 0;
for (auto it : S){
int hei = it.first,pos = -it.second;
for (Int h1 = 0;h1 <= c1[pos - 1];++ h1)
if (s1[pos - 1][h1] < hei)
for (Int h2 = 0;h2 <= c2[pos + 1];++ h2)
if (s2[pos + 1][h2] <= hei)
for (Int c = 0;c <= K;++ c)
Add (res,add (mul (f[pos - 1][h1][c][0],g[pos + 1][h2][K - c][0]),mul (f[pos - 1][h1][c][1],g[pos + 1][h2][K - c][1])));
}
write (res),putchar ('\n');
return 0;
}