Live2D

题解 暴雨

link

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;
}
posted @ 2021-10-14 15:27  Dark_Romance  阅读(53)  评论(0编辑  收藏  举报