[HDU6240]Server

题目大意:
  用$n$条线段覆盖区间$[1,t]$上的整点。每条线段有4个属性$(S_i,T_i,A_i,B_i)$,表示用第$i$条线段可以覆盖区间$[S_i,T_i]$。若选取线段的集合为$S$,最后总代价为$\frac{\sum_{i\in S}A_i}{\sum_{i\in S}B_i}$。问区间$[1,t]$全部覆盖时,最小代价为多少?

思路:
  分数规划经典模型。
  二分答案$k$,表示代价为$k$。若$k$为可行的,则我代价们需要找到一个合法的$S$,满足$\frac{\sum_{i\in S}A_i}{\sum_{i\in S}B_i}\leq k$。移项得$\sum_{i\in S}(A_ik-B_i)\geq 0$。
  可以将所有线段按照左端点排序,然后考虑每个线段对答案的贡献。
  若$A_ik-B_i\geq 0$,则选了这个线段肯定能够让答案尽可能大于$0$,因此把它选上。
  如果把所有$A_ik-B_i\geq 0$的线段选上后能够覆盖完整个区间,那么$k$为可行的代价。
  如果不能覆盖完,那么我们要考虑选上一些$A_ik-B_i<0$的线段,使得代价增加最少的情况下,能把区间覆盖完。
  若用$sum$表示必选线段$A_ik-B_i$的和,$f_i$表示覆盖完$i$点后,$sum$要减少的值。那么对于必选线段$i$,$f_{r_i}=\min\{f_j\mid S_i\leq j\leq t\}$(选了以后并不会影响$sum$),对于其它线段,$f_{r_i}=\min\{f_j|S_i\leq j\leq t\}+B_i-A_ik$。由于$f_{r_i}$,可能会被计算多次,因此取最小值即可。转移的时候相当于后缀最小值,显然可以使用树状数组优化。最后只需要判断$sum-f_t\geq 0$即可。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<algorithm>
 4 inline int getint() {
 5     register char ch;
 6     while(!isdigit(ch=getchar()));
 7     register int x=ch^'0';
 8     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
 9     return x;
10 }
11 const int N=100001;
12 const double eps=1e-5,inf=1e10;
13 struct Node {
14     int l,r,w,a;
15     bool operator < (const Node &another) const {
16         return l<another.l||(l==another.l&&r<another.r);
17     }
18 };
19 Node node[N];
20 int n,m;
21 struct SuffixFenwickTree {
22     double val[N];
23     int lowbit(const int &x) const {
24         return x&-x;
25     }
26     void reset() {
27         for(register int i=1;i<=m;i++) {
28             val[i]=-inf;
29         }
30     }
31     void modify(int p,const double &x) {
32         while(p) {
33             val[p]=std::max(val[p],x);
34             p-=lowbit(p);
35         }
36     }
37     double query(int p) const {
38         if(!p) return 0;
39         double ret=-inf;
40         while(p<=m) {
41             ret=std::max(ret,val[p]);
42             p+=lowbit(p);
43         }
44         return ret;
45     }
46 };
47 SuffixFenwickTree t;
48 inline bool check(const double &k) {
49     t.reset();
50     int last=0;
51     for(register int i=1;i<=n;i++) {
52         if(node[i].a*k-node[i].w<0) continue;
53         if(node[i].l-1<=last) last=std::max(last,node[i].r);
54     }
55     if(last>=m) return true;
56     double sum=0;
57     t.modify(last,0);
58     for(register int i=1;i<=n;i++) {
59         if(node[i].a*k-node[i].w<0) {
60             t.modify(node[i].r,t.query(node[i].l-1)+node[i].a*k-node[i].w);
61         } else {
62             sum+=node[i].a*k-node[i].w;
63             t.modify(node[i].r,t.query(node[i].l-1));
64         }
65     }
66     return sum+t.query(m)>=0;
67 }
68 int main() {
69     for(register int T=getint();T;T--) {
70         n=getint(),m=getint();
71         int sumw=0,suma=0;
72         for(register int i=1;i<=n;i++) {
73             const int l=getint(),r=getint(),w=getint(),a=getint();
74             sumw+=w,suma+=a;
75             node[i]=(Node){l,r,w,a};
76         }
77         std::sort(&node[1],&node[n+1]);
78         double l=0,r=sumw*1./suma;
79         while(r-l>=eps) {
80             const double mid=(l+r)/2;
81             (check(mid)?r:l)=mid;
82         }
83         printf("%.3f\n",r);
84     }
85     return 0;
86 }

 

转载于:https://www.cnblogs.com/skylee03/p/8275829.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值