题目大意:
用$n$条线段覆盖区间$[1,t]$上的整点。每条线段有4个属性$(S_i,T_i,A_i,B_i)$,表示用第$i$条线段可以覆盖区间$[S_i,T_i]$。若选取线段的集合为$S$,最后总代价为$frac{sum_{iin S}A_i}{sum_{iin S}B_i}$。问区间$[1,t]$全部覆盖时,最小代价为多少?
思路:
分数规划经典模型。
二分答案$k$,表示代价为$k$。若$k$为可行的,则我代价们需要找到一个合法的$S$,满足$frac{sum_{iin S}A_i}{sum_{iin S}B_i}leq k$。移项得$sum_{iin S}(A_ik-B_i)geq 0$。
可以将所有线段按照左端点排序,然后考虑每个线段对答案的贡献。
若$A_ik-B_igeq 0$,则选了这个线段肯定能够让答案尽可能大于$0$,因此把它选上。
如果把所有$A_ik-B_igeq 0$的线段选上后能够覆盖完整个区间,那么$k$为可行的代价。
如果不能覆盖完,那么我们要考虑选上一些$A_ik-B_i<0$的线段,使得代价增加最少的情况下,能把区间覆盖完。
若用$sum$表示必选线段$A_ik-B_i$的和,$f_i$表示覆盖完$i$点后,$sum$要减少的值。那么对于必选线段$i$,$f_{r_i}=min{f_jmid S_ileq jleq t}$(选了以后并不会影响$sum$),对于其它线段,$f_{r_i}=min{f_j|S_ileq jleq t}+B_i-A_ik$。由于$f_{r_i}$,可能会被计算多次,因此取最小值即可。转移的时候相当于后缀最小值,显然可以使用树状数组优化。最后只需要判断$sum-f_tgeq 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 ",r); 84 } 85 return 0; 86 }