ICPC2020上海站F Fountains 状压

题意:给定长为\(n\)的序列,定义区间\([l,r]\)的权值\(C(l,r)\)\([l,r]\)中所有元素的和,现在试选择\(k\)个区间\([l_i, r_i](1\leq l_i\leq r_i\leq n)\),使得
$$\sum_{1\leq L\leq R\leq n} [C(L, R) - \max_{L\leq l_i \leq r_i \leq R } C(l_i, r_i)]$$最小,对\(k=1, 2, 3..., n*(n+1)/2\)作出回答,且\(n\leq 9\)


不妨假定已经选择\(k\)个区间,考虑怎么使得权值和最小

显然,将\(k\)个区间按权值大小从大到小排序,所有的\([L, R]\)区间会按此顺序选择第一个被它包含的区间\([l_i, r_i]\)

换而言之,即将\(k\)个区间从大到小排序后,依次考虑,在仍未选择的区间\([L, R]\)中,所有能选择\([l_i, r_i]\)的区间一定会选择此区间

可以\(dp\),状态为已经选择了\(k\)个区间,及仍未选择的区间

仍未选择的区间可以通过\((r_1, r_2, ..., r_n)\)表示,其中\(r_i\)表示\([i, i], [i,i+1], ..., [i, r_i]\)仍未选择

由于\(r_1\leq r_2 \leq ... \leq r_n\),状态数并不大(最大375544),并且本题有6s时限,hash都不用,开个map即可过

#include <map>
#include <vector>
#include <cstdio>
#include <iostream>
#include <algorithm> 
using namespace std;

#define l first
#define r second
#define ll long long
#define mp make_pair
#define pii pair <int, int>
#define rep(io, st, ed) for(int io = st; io <= ed; io ++)
#define drep(io, ed, st) for(int io = ed; io >= st; io --)

const int sid = 1e6 + 5;

int n, tt[105];
ll all, ans[105], s[105];
map <ll, ll> now, pre;
vector <pii> seg;

ll encode(int *a) {
	ll ret = 0;
	drep(i, n + 1, 1) ret = ret * 10 + a[i];
	return ret;
}

void decode(ll v, int *a) {
	rep(i, 1, n) a[i] = v % 10, v /= 10;
	a[n + 1] = v;
}

int main() {
	cin >> n;
	rep(i, 1, n) cin >> s[i], s[i] += s[i - 1];
	rep(L, 1, n) rep(R, L, n) seg.push_back(mp(L, R)), all += s[R] - s[L - 1];
	sort(seg.begin(), seg.end(), [](pii &a, pii &b) { return s[a.r] - s[a.l - 1] >= s[b.r] - s[b.l - 1]; } );
	rep(i, 1, n) tt[i] = n; now[encode(tt)] = 0;
	rep(si, 0, seg.size() - 1) {
		int l = seg[si].l, r = seg[si].r;
		ll v = s[r] - s[l - 1];
		pre = now; now.clear();
		for(auto x : pre) {
			ll pv = x.second; decode(x.first, tt);
			//不选择
			now[x.first] = max(now[x.first], pv);
			//选择
			rep(i, 1, l) if(tt[i] >= r) pv += (tt[i] - r + 1) * v, tt[i] = r - 1;
			tt[n + 1] ++; ll to = encode(tt);
			now[to] = max(now[to], pv);
		}
	}
	for(auto x : now) {
		decode(x.first, tt);
		ans[ tt[n + 1] ] = max(ans[ tt[n + 1] ], x.second);
	}
	rep(i, 1, n * (n + 1) / 2) printf("%lld\n", all - ans[i]);
	return 0;
}
posted @ 2020-12-18 11:05  remoon  阅读(358)  评论(0编辑  收藏  举报