题解 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;
}