[BZOJ1701] [Usaco2007 Jan]Cow School牛学校

[BZOJ1701] [Usaco2007 Jan]Cow School牛学校

可以说是一道变异的01规划

其实就是问是否存在方案满足不是选\(t/p\)最高的最优

设当前选了这些点,总答案是\(s\),即\(\sum t_i-p_i*s = 0\)

如果存在方案,则必然满足存在集合S \(\sum_{i \in S}t_i-p_i*s>0\)

然而,其实只要把选了的点中\(t_i-p_i*s\)最小的换成没选的中\(t_i-p_is\)中最大的,看看是否大于0即可

所以可以用斜率优化求最值,由于查询不具有单调性,所以要用 分治+单调栈+二分

两边求解后统计答案即可

const int N=5e4+10,P=1e9+7;

int n;
struct Node{ 
    int a,b;
    bool operator < (const Node __)const{ return 1.0*a/b>1.0*__.a/__.b; }
}A[N],B[N];
bool cmp(Node a,Node b){
    return a.a<b.a;
}
 
double sa[N],sb[N];
double res[N];
double ma[N],mi[N];
double QA[N],QB[N];
int L,R;
 
void Solve1(int l,int r) {
    if(l>=r) return;
    int mid=(l+r)>>1;
    rep(i,l,mid) B[i]=(Node){A[i].b,A[i].a};
    sort(B+l,B+mid+1,cmp);
    L=1,R=0;
    rep(i,l,mid) {
        while(L<R && (QB[R]-QB[R-1])/(QA[R]-QA[R-1])>(B[i].b-QB[R])/(B[i].a-QA[R]) ) R--;
        QB[++R]=B[i].b;
        QA[R]=B[i].a;
    }
    rep(i,mid+1,r) {
        double t=res[i-1];
        int bl=2,br=R,bmid,bres=1;
        while(bl<=br) {
            bmid=(bl+br)>>1;
            if((QB[bmid]-QB[bmid-1])/(QA[bmid]-QA[bmid-1])<=t) bl=bmid+1,bres=bmid;
            else br=bmid-1;
        }
        mi[i-1]=min(mi[i-1],QB[bres]-t*QA[bres]);
    }
    Solve1(l,mid);
    Solve1(mid+1,r);
}
 
void Solve2(int l,int r) {
    if(l>=r) return;
    int mid=(l+r)>>1;
    rep(i,mid+1,r) B[i]=(Node){A[i].b,A[i].a};
    sort(B+mid+1,B+r+1,cmp);
    L=1,R=0;
    rep(i,mid+1,r) {
        while(L<R && (QB[R]-QB[R-1])/(QA[R]-QA[R-1])<(B[i].b-QB[R])/(B[i].a-QA[R]) ) R--;
        QB[++R]=B[i].b;
        QA[R]=B[i].a;
    }
    rep(i,l,mid) {
        double t=res[i];
        int bl=2,br=R,bmid,bres=1;
        while(bl<=br) {
            bmid=(bl+br)>>1;
            if((QB[bmid]-QB[bmid-1])/(QA[bmid]-QA[bmid-1])>=t) bl=bmid+1,bres=bmid;
            else br=bmid-1;
        }
        ma[i]=max(ma[i],QB[bres]-t*QA[bres]);
    }
    Solve2(l,mid);
    Solve2(mid+1,r);
}
int cnt;
 
int main(){
    rep(i,1,n=rd()) A[i].a=rd(),A[i].b=rd();
    sort(A+1,A+n+1);
    rep(i,1,n) {
        sa[i]=sa[i-1]+A[i].a,sb[i]=sb[i-1]+A[i].b;
        ma[i]=-1e18;
        mi[i]=1e18;
    }
    rep(i,1,n) res[i]=sa[i]/sb[i];
    Solve1(1,n+1);
    Solve2(1,n);
    drep(i,n-1,1) if(ma[i]>mi[i]) cnt++;
    printf("%d\n",cnt);
    drep(i,n-1,1) if(ma[i]>mi[i]) printf("%d\n",n-i);
}
 
 
原文地址:https://www.cnblogs.com/chasedeath/p/11743206.html