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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值