[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_i\ge Min_a\)
3.\(b_i\ge Min_b\)
很容易想到\(n^3\)暴力,枚举\(Min_a\)\(Min_b\),再扫一遍考虑优化它。
我们发现,如果同时满足1,3,那么可以得出4.\(a_i \le Min_a+ \lfloor \frac{C}{A} \rfloor\)
注意到这是可以逆推回去的,那么对于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\n",ans);
    return 0;
}

用时:2h

转载于:https://www.cnblogs.com/functionendless/p/9441437.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值