Server
Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 1540 Accepted Submission(s): 256
,其中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;
}