题解 drop

传送门
更加详细的讲解

  • 一些每个数贡献与前/后缀最大值相关的问题可以将数按从大到小的顺序加入序列,可以保证每次加入的数一定是当前序列中最大的

于是DP方式和解释与题解是一样的
大意是对水+柱子的总体积做背包
若当前这个是 \(i\),考虑 \(i+1\)\(i\) 之间有多少个柱子,就有多少贡献
然后从小到大对每个柱子做一遍即可
这个DP很特别,建议再看一次

然后这个东西复杂度是 \(O(n^2\sum h)\) 的,还可以优化
发现高度相等的柱子可以用一个类似完全背包的思想进行优化
?建议再看一遍
具体地,可以从大到小枚举,对个数记录一个后缀和,在对值域上的一个值进行背包的时候将不合法的reset掉即可
不合法即后面有柱子没有选择,这个可以后缀和判断
这样复杂度就是 \(O(\frac{n\sum h}{w})\) 的了

Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 510
#define ll long long
// #define int long long

char buf[1<<21], *p1=buf, *p2=buf;
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;
int h[N];

namespace force{
	int p[N], tem[N], pre[N], suf[N], uni[N], usiz;
	void solve() {
		for (int i=1; i<=n; ++i) p[i]=i;
		do {
			int sum=0;
			for (int i=1; i<=n; ++i) tem[i]=h[p[i]];
			for (int i=1; i<=n; ++i) pre[i]=max(pre[i-1], tem[i]);
			for (int i=n; i; --i) suf[i]=max(suf[i+1], tem[i]);
			for (int i=1,lim; i<=n; ++i) {
				lim=min(pre[i-1], suf[i+1]);
				if (lim>tem[i]) sum+=lim-tem[i];
			}
			uni[++usiz]=sum;
		} while (next_permutation(p+1, p+n+1));
		sort(uni+1, uni+usiz+1);
		usiz=unique(uni+1, uni+usiz+1)-uni-1;
		for (int i=1; i<=usiz; ++i) printf("%d%c", uni[i], " \n"[i==usiz]);
		exit(0);
	}
}

namespace task1{
	int uni[N], usiz, sum, m;
	bool f[N][N*50];
	void solve() {
		for (int i=1; i<=n; ++i) uni[i]=h[i], sum+=h[i];
		sort(uni+1, uni+n+1);
		// usiz=unique(uni+1, uni+n+1)-uni-1;
		f[0][0]=1;
		for (int j=1; j<n; ++j) {
			for (int i=1; i<=j; ++i) {
				for (int v=uni[j]; v<=n*m; ++v) {
					f[i][v]|=f[i-1][v-uni[j]];
				}
			}
		}
		sum-=uni[n];
		for (int i=sum; i<=n*50; ++i) if (f[n-1][i]) printf("%d ", i-sum);
		printf("\n");
		exit(0);
	}
}

namespace task{
	int uni[N], buc[N], suf[N], sum, m;
	bitset<N*50> f[N];
	void solve() {
		sort(h+1, h+n+1); --n;
		for (int i=1; i<=n; ++i) uni[i]=h[i], sum+=h[i], ++buc[h[i]], m=max(m, h[i]);
		// cout<<"m: "<<m<<endl;
		for (int i=m; i; --i) suf[i]=suf[i+1]+buc[i];
		f[0][0]=1;
		for (int i=m; i; --i) if (buc[i]) {
			for (int j=1; j<=n; ++j) f[j]|=f[j-1]<<i;
			for (int j=0; j<suf[i]; ++j) f[j].reset();
		}
		for (int i=0; i<=n*50; ++i) if (f[n][i]) printf("%d ", i-sum);
		printf("\n");
		exit(0);
	}
}

signed main()
{
	freopen("drop.in", "r", stdin);
	freopen("drop.out", "w", stdout);

	n=read();
	for (int i=1; i<=n; ++i) h[i]=read();
	// force::solve();
	task::solve();

	return 0;
}
posted @ 2021-11-19 15:29  Administrator-09  阅读(1)  评论(0编辑  收藏  举报