HDU 6240 Server(2017 CCPC哈尔滨站 K题,01分数规划 + 树状数组优化DP)

题目链接  2017 CCPC Harbin Problem K

题意  给定若干物品,每个物品可以覆盖一个区间。现在要覆盖区间$[1, t]$。

   求选出来的物品的$frac{∑a_{i}}{∑b_{i}}$的最小值。

 

首先二分答案,那么每个物品的权值就变成了$x * b_{i} - a_{i}$

在判断的时候先把那些权值为正的物品全部选出来,

然后记录一下从$1$开始可以覆盖到的最右端点的位置。

接下来开始DP,按照区间的端点升序排序(左端点第一关键字,右端点第二关键字)

问题转化为能否用剩下的物品覆盖尚未覆盖的区间。

那么直接用树状数组优化DP就可以了。

#include <bits/stdc++.h>

using namespace std;

#define	rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define	dec(i, a, b)	for (int i(a); i >= (b); --i)
#define	fi		first
#define	se		second

const int N = 1e5 + 10;

struct node{
	int s, t;
	double a, b;
	bool flag;
	void scan(){ scanf("%d%d%lf%lf", &s, &t, &a, &b); }

	friend bool operator < (const node &a, const node &b){
		return a.s == b.s ? a.t < b.t : a.s < b.s;
	}
} a[N];

int T;
int n, m;
double c[N];
double l, r;


void update(int x, double val){
	for (; x; x -= x & -x) c[x] = min(c[x], val);
}

double query(int x){
	if (x == 0) return 0;
	double ret = 1e9;
	for (; x <= m; x += x & -x) ret = min(ret, c[x]);
	return ret;
}

bool check(double x){
	int mx = 0;
	double ss = 0;
	rep(i, 0, m) c[i] = 1e9;

	rep(i, 1, n){
		if (a[i].b * x - a[i].a > 0){
			ss += a[i].b * x - a[i].a;
			a[i].flag = true;

			if (a[i].s - 1 <= mx){
				mx = max(mx, a[i].t);
			}
		}

		else a[i].flag = false;
	}

	if (mx == m) return true;
	update(mx, 0);

	rep(i, 1, n){
		if (!a[i].flag){
			double mi = query(a[i].s - 1);
			update(a[i].t, mi + a[i].a - a[i].b * x);
		}

		else{
			double mi = query(a[i].s - 1);
			update(a[i].t, mi);
		}
	}

	return ss >= query(m);
}


int main(){

	scanf("%d", &T);
	while (T--){
		scanf("%d%d", &n, &m);
		rep(i, 1, n) a[i].scan();
		sort(a + 1, a + n + 1);

		l = 0, r = 1e3 + 10;

		rep(i, 1, 50){
			double mid = (l + r) / 2.00;
			if (check(mid)) r = mid;
			else l = mid;
		}

		printf("%.3f
", l);
	}

	return 0;

}