[SCOI2007] 组队

题目传送-Luogu4165

题目传送-BZOJ1071

题意:

(n)个物品,每个物品有属性(a,b)
同时给出整数(A,B,C)
(n)个物品中选出最多的物品,使得对于其中任意一个物品(i),有(A*(a_i-Min_a)+B*(b_i-Min_b)le C)
其中(Min_a)为选中的物品中(a)的最小值,(Min_b)同理
$n le 5000 $,所有运算在长整型范围中.

题解:

本题的实质是取若干数,使其满足如下不等式:
1.(A*(a_i-Min_a)+B*(b_i-Min_b)le C)
2.(a_ige Min_a)
3.(b_ige Min_b)
很容易想到(n^3)暴力,枚举(Min_a)(Min_b),再扫一遍考虑优化它。
我们发现,如果同时满足1,3,那么可以得出4.(a_i le Min_a+ lfloor frac{C}{A} floor)
注意到这是可以逆推回去的,那么对于b的限制就转换成对于a的限制了
那么如果确定了(Min_a)(a)的范围也确定了
再对1移项整理,得(A*a_i+B*b_i le C+A*Min_a+B*Min_b)
定义(border=C+A*Min_a+B*Min_b)
所以枚举(Min_a)(border)就可以(n^2)得出答案了

过程:

刚开始的时候没有发现4是充要的。。然后写出了下面两行注释,发现这个是错的。
因为虽然一开始符合条件,但随着枚举的进行却会不符合

代码:

const int N=5010;
int n; ll A,B,C;

struct PEOPLE {
    ll a,b,v;
    inline void in() {
        read(a); read(b); v=a*A+b*B;
    }
    bool operator < (const PEOPLE &_)const {
        return b<_.b;
    }
}p[N],q[N];
bool cmp(const PEOPLE &x,const PEOPLE &y) {
    return x.v<y.v;
}
signed main() {
    read(n); read(A); read(B); read(C);
    for(int i=1;i<=n;i++) p[i].in(),q[i]=p[i];
    sort(p+1,p+n+1); sort(q+1,q+n+1,cmp);
    int ans=0;
    for(int i=1;i<=n;i++) {
    	int L=p[i].a,R=p[i].a+C/A;
        int p1=1,ret=0;
        for(int j=1;j<=n;j++) {
            while(p1<=n && q[p1].v<=C+A*p[i].a+B*p[j].b) {
                if(L<=q[p1].a && q[p1].a<=R) ++ret;
                // if(q[p1].a>=p[i].a) ++ret;
                ++p1;
            }
            if(L<=p[j-1].a && p[j-1].a<=R) --ret;
            // if(p[j-1].a>=p[i].a && Calc(p[j-1])<=C+A*p[i].a+B*p[j].b) {assert(p[j-1].a>C/A+p[j].a); --ret;}
            ans=max(ans,ret);
        }
    }
    printf("%d
",ans);
    return 0;
}

用时:2h

原文地址:https://www.cnblogs.com/functionendless/p/9441437.html