【BZOJ1071】【SCOI2007】组队 利用单调性的双指针

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/45746089");
}

题解:

三个定义:高度h,v速度,Ah+Bv为s
首先我们在外圈枚举来固定其中一个权值,姑且枚举v吧。每次枚举值大写为V。
然后在内圈就可以做单调队列了。

我们枚举让h递增,每次为H,然后发现原来的式子要满足 Ah+Bv<=C+AH+BV

然后 H增大会使得右式增大,然后如果我们按s(定义去题解第一句话找)为键值从小到大排序,那么满足此式的队员是单调上升的。

然后 我们单调地每次把满足此式子的队员加入队列,判断如果其v值满足条件 V<=v<=V+CB ,则把它计数,即此种枚举情况时的ans++。( v<=V+CB 为什么对?如果 v>V+CB ,则 BvBV>C 然后你懂得)【加入环节】

然后 会有一些被计数的队员身高式子存在 h<H ,我们从前往后单调地把身高不满足上式的出队,如果之前被计数了,则此种枚举情况时的ans- -。【弹出环节】

这样比较一下这 n2 个答案,我们就在 O(n2) 的时间复杂度下解决了这道题。

但是?【弹出环节】是否会弹出一些【加入环节】没有加入的点呢?

让我来猜一猜,你的答案一定是”不会“吧?
然而答案是会,但是这种点并不会影响答案。

分类讨论下
首先如果一个点满足被计数的条件即 V<=v<=V+CB ,那么如果又满足 h<H 那么有 0+B(vV)<=C ,而 A(hH)<0
所以 A(hH)+B(vV)<=C 。。。。。。所以一定加进来然后被计数过,不会减多了。

然后如果不满足被计数的条件。23333,都没有被计数我们管它作甚?

代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 5050
using namespace std;
int n,A,B,C;
int Max,Min,l,r,cnt,ans;
struct KSD
{
    int h,v,s;
    void keep(){s=A*h+B*v;}
}x[2][N];
inline bool cmph(const KSD &a,const KSD &b){return a.h<b.h;}
inline bool cmps(const KSD &a,const KSD &b){return a.s<b.s;}
inline bool check(int id,int d)
{return x[id][d].v<=Max&&x[id][d].v>=Min;}
int main()
{
    int i,j,k;
    scanf("%d%d%d%d",&n,&A,&B,&C);
    for(i=1;i<=n;i++)
    {
        scanf("%d%d",&x[0][i].h,&x[0][i].v);
        x[0][i].keep(),x[1][i]=x[0][i];
    }
    sort(x[0]+1,x[0]+n+1,cmph);
    sort(x[1]+1,x[1]+n+1,cmps);
    for(i=1;i<=n;i++) // 枚举v最小值
    {
        Min=x[0][i].v,Max=Min+C/B;
        l=r=cnt=0;
        for(j=1;j<=n;j++) // 枚举h最小值
        {
            while(r<n&&x[1][r+1].s-A*x[0][j].h-B*x[0][i].v<=C)
                cnt+=check(1,++r);//按照s排序取出可行队员
            while(l<n&&x[0][l+1].h<x[0][j].h)cnt-=check(0,++l);
            ans=max(ans,cnt);//再干掉一些当初入队时计数了的队员
        }
    }
    printf("%d\n",ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值