题解 暴雨

传送门

神仙题,想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;
}
posted @ 2021-10-14 06:36  Administrator-09  阅读(0)  评论(0编辑  收藏  举报