BZOJ 4167: 永远的竹笋采摘

首先同BZOJ5052 \(O(n \log n \log v)\) 求出所有点对
现在变成选出 \(k\) 条不相交的线段使得权值最小
可用前缀min优化dp \(O(nk)\) 解决
还是太慢,考虑wqs二分,大胆猜想 \(dp_k\) 斜率单调,通过二分斜率限制一下取的线段条数即可

#include <bits/stdc++.h>
#define pb push_back
#define fi first
#define se second
#define pii pair<ll, int>
#define lp p << 1
#define rp p << 1 | 1
#define mid ((l + r) / 2)
#define lowbit(i) ((i) & (-i))
#define ll long long
#define ull unsigned long long
#define db double
#define rep(i,a,b) for(int i=a;i<b;i++)
#define per(i,a,b) for(int i=b-1;i>=a;i--)
#define Edg int ccnt=1,head[N],to[N*2],ne[N*2];void addd(int u,int v){to[++ccnt]=v;ne[ccnt]=head[u];head[u]=ccnt;}void add(int u,int v){addd(u,v);addd(v,u);}
#define Edgc int ccnt=1,head[N],to[N*2],ne[N*2],c[N*2];void addd(int u,int v,int w){to[++ccnt]=v;ne[ccnt]=head[u];c[ccnt]=w;head[u]=ccnt;}void add(int u,int v,int w){addd(u,v,w);addd(v,u,w);}
#define es(u,i,v) for(int i=head[u],v=to[i];i;i=ne[i],v=to[i])
const int MOD = 1e9 + 7;
void M(int &x) {if (x >= MOD)x -= MOD; if (x < 0)x += MOD;}
int qp(int a, int b = MOD - 2) {int ans = 1; for (; b; a = 1LL * a * a % MOD, b >>= 1)if (b & 1)ans = 1LL * ans * a % MOD; return ans % MOD;}
int gcd(int a, int b) { while (b) { a %= b; std::swap(a, b); } return a; }
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline char getc() {
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
}
inline int _() {
	int x = 0, f = 1; char ch = getc();
	while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getc(); }
	while (ch >= '0' && ch <= '9') { x = x * 10ll + ch - 48; ch = getc(); }
	return x * f;
}

const int N = 1e5 + 7;
const int INF = 0x3f3f3f3f;
int n, k, a[N], b[N], mn[N << 2];
inline bool cmp(int x, int y) {
	return a[x] == a[y] ? x > y : a[x] > a[y];
}
void update(int p, int l, int r, int pos, int v) {
	mn[p] = std::min(mn[p], v);
	if (l == r) return;
	if (pos <= mid) update(lp, l, mid, pos, v);
	else update(rp, mid + 1, r, pos, v);
}
int query(int p, int l, int r, int pos, int v) {
	if (mn[p] > v) return 0;
	if (l == r) return l;
	if (pos <= mid) {
		int t = query(lp, l, mid, pos, v);
		if (t) return t;
	}
	return query(rp, mid + 1, r, pos, v);
}

struct Node {
	int l, r, v;
	Node() {}
	Node(int x, int y, int va) {
		if (x > y) std::swap(x, y);
		if (va < 0) va *= -1;
		l = x, r = y, v = va;
	}
	bool operator < (const Node &p) const {
		return l < p.l;
	}
} q[N * 20];
int tol;

void init() {
	std::sort(b + 1, b + 1 + n, cmp);
	memset(mn, 0x3f, sizeof(mn));
	rep (i, 1, n + 1) {
		int j = query(1, 1, n, b[i], INF - 1);
		while (j) {
			if (a[j] != a[b[i]]) q[++tol] = Node(b[i], j, a[j] - a[b[i]]);
			j = query(1, 1, n, b[i], (a[b[i]] + a[j] - 1) / 2);
		}
		update(1, 1, n, b[i], a[b[i]]);
	}
}
int cnt[N * 20], pos[N], res;
db dp[N * 20], cost;
void update(int &a, int b) { if (dp[a] > dp[b]) a = b; }
void check() {
	memset(pos, 0, sizeof(int) * (n + 2));
	int last = res = 0;
	rep (i, 1, tol + 1) {
		while (last + 1 < q[i].l) {
			last++;
			update(pos[last], pos[last - 1]);
		}
		dp[i] = dp[pos[last]] + q[i].v + cost;
		cnt[i] = cnt[pos[last]] + 1;
		update(res, i);
		update(pos[q[i].r], i);
	}
}

int main() {
	n = _(), k = _();
	rep (i, 1, n + 1) a[i] = _(), b[i] = i;
	init();
	rep (i, 1, n + 1) a[i] = n - a[i] + 1;
	init();
	std::sort(q + 1, q + 1 + tol);
	db l = -1e9, r = 1e9;
	rep (i, 0, 88) {
		cost = mid;
		check();
		if (cnt[res] == k) break;
		if (cnt[res] < k) r = mid;
		else l = mid;
	}
	printf("%.0f\n", dp[res] - cost * k);
	return 0;
}
posted @ 2020-02-28 14:10  Mrzdtz220  阅读(114)  评论(0编辑  收藏  举报