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

题目传送门

Server

Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1540    Accepted Submission(s): 256


 

Problem Description
Alice and Bob are working on a new assignment. In this project, they need to access some information on a website and monitor this web site for consistent t days. In others words, in each day, there must be at least one server in work. Luckily, they can rent some servers in the lab. According to the schedule, there are totally N servers being available. The i-th server can be used from the Si-th day to the Ti-th day. However, using the i-th server require Ai dollars. After a long time of persuasion, the administrator of the machine room agree to give Alice and Bob discount. Each server is assigned with a discount number Bi. If the set of servers they want to use is S, they need to pay ∑i∈SAi∑i∈SBi dollars. As Alice and Bob are preparing the programs on the servers, they want your help to find a way to minimize the cost of servers.
 

 

Input
The first line is the number of test cases. For each test case, the first contains two positive integers N and t (N≤100000,t≤100000) as described above. In the next N lines, the i-th line consists of four integer Si, Ti, Ai, and Bi (1≤Si≤Ti≤t,0<Ai,Bi≤1000).
 

 

Output
For each test case, output an float-point number with three decimal places donating the minimum cost Alice and Bob need to pay. It is guaranteed that there is at least one feasible way to rent servers.
 

 

Sample Input

 
1 4 5 1 3 28 1 4 5 22 1 3 5 34 1 1 2 26 1
 

 

Sample Output

 
25.000
 

 

Source
 

 

Recommend
jiangzijing2015   |   We have carefully selected several similar problems for you:  6742 6741 6740 6739 6738 
 
 
题意:给定n(<=1e5)个物品和t(<=1e5),每种物品有四个属性l,r,a,b(a,b<=1000)。
其中l,r表示这个物品可以覆盖区间[l,r]。r最大为t。
现在让你选出若干个物品覆盖[1,t],保证答案存在,求最小的

\frac{\sum_{i\epsilon S}a_{i}}{\sum_{i\epsilon S}b_{i}},其中S为被选中物品的下标的集合。

思路:

很容易想到是01分数规划,二分答案mid,然后每个物品的价值就是bi*mid-ai。

对于价值大于0的物品,我们直接选择它并记录价值和sum、能覆盖到的最右端点mx。

这时问题转化成了,从剩下的物品中,用尽可能小的花费覆盖[1,t]。由于题目保证答案存在,因此我们只考虑覆盖t点最小花费。

按区间左端点为第一关键字,右端点为第二关键字排序。这样就可以使用线段树/树状数组优化dp来做。

具体做法:

处理完sum和mx后,将其他点的价值处理为inf,mx点的价值处理为0。

依次遍历i=1~n,如果价值大于零,直接用[ a[i].l-1 , t ]中的最小花费更新a[i].r点的最小花费,否则用[ a[i].l-1 , t ]中的最小花费-(bi*mid-ai)更新a[i].r点的最小花费,最后判断sum是否大于覆盖t点的花费即可。

总体复杂度为O(nlogn)或O(nlogt),还有一个二分的常数。

代码:
#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define mst(head,x,n) memset(head+1,x,n*sizeof(head[0]))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;
const int maxn=2e5+5;
//const double pi=acos(-1.0);
const double eps=1e-5;
//const ll mo=1e9+7;
int n,m,k;
double ans,tmp,cnt;
int flag;
double c[maxn];
template <typename T>
inline void read(T &X){
    X=0;int w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    if(w) X=-X;
}
struct node{
    int l,r;
    double xa,xb;
    int fg;
    bool operator<(node aa)const{
        return l<aa.l||(l==aa.l&&r<aa.r);
    }
}a[maxn];
int lb(int x){return x&(-x);}
void add(int x,double v){
    while(x>0){
        c[x]=min(c[x],v);
        x-=lb(x);
    }
}
double query(int x){
    if(x==0) return 0;
    double ans=inf;
    while(x<=k){
        ans=min(ans,c[x]);
        x+=lb(x);
    }
    return ans;
}
int jud(double md){
    double sum=0;
    int mx=0;
    rep(i,1,k) c[i]=inf;
    rep(i,1,n){
        if(a[i].xb*md>a[i].xa){
            sum+=a[i].xb*md-a[i].xa;
            if(a[i].l-1<=mx) mx=max(mx,a[i].r);
            a[i].fg=1;
        }
        else a[i].fg=0;
    }
    add(mx,0);
    rep(i,1,n) {
        if(a[i].fg){
            double s=query(a[i].l-1);
            add(a[i].r,s);
        }
        else {
            double s=query(a[i].l-1);
            add(a[i].r,s-a[i].xb*md+a[i].xa);
        }
    }
    return sum>=query(k);
}
int main(){
    int T,cas=1;
    read(T);
    while(T--)
    {
        read(n);read(k);
        rep(i,1,n){
            scanf("%d%d%lf%lf",&a[i].l,&a[i].r,&a[i].xa,&a[i].xb);
        }
        sort(a+1,a+1+n);
        double l=0,r=1001;
        while(r-l>eps){
            double mid=(l+r)/2.0;
            if(jud(mid)) r=mid;
            else l=mid;
        }
        printf("%.3f\n",l);
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章