Educational Codeforces Round 105 (Rated for Div. 2)C (二分+思维)

C. 1D Sokoban
这题是一道相当好的二分思维题啊,题意很秒,思维很难想到。口胡: 必须写下来留着以后出题
上正文。
题意:人在一个一维的坐标上,处于0位置,然后左右都有箱子共N个 每个箱子位置为a[i],然后一维坐标上有完美位置b[i],共有M个,之后人会推动箱子去完美位置,但是人不可以越过面前的箱子去推后面的,如果遇到了下一个,那么箱子就会+1个,一起推动,假如最右边的箱子在最右边的完美位置,那么最右边的那个箱子就可以不动,问这样左右都推箱子后,可以得到最多的完美位置上有箱子的个数是多少?(推动次数无线,再多箱子都可以推动)

题解:
5 6
-1 1 5 11 15
-4 -3 -2 6 7 15样例1

题意实际上很大坑,如果没有读懂绝对使劲wa,题意要求箱子不能跳跃推,所以我们可以考虑如何去处理每个完美位置涵盖了多少箱子,首先枚举每个完美位置左边有多少个箱子可以推过来,然后可以找到有up个推过来,之后我们就要在完美位置上找这个几个连续箱子以当前完美位置为终点,向左边找到起点,当前枚举完美位置i,那么起点就是 i-up+1,就是他的起点位置,实现这两个过程就需要使用stl 里面的lower_bound 和upper_bound函数直接去查询 不懂得去看这个大佬博客这两个函数
枚举这个区间,如果是最优 那么我们就应直接计算后面不移动得箱子本来就在完美位置得数量,所以在这样处理前 先处理一下后缀和,然后再去枚举,左边得箱子 反向处理下就可以了 也是同样道理。

#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define fio ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define mse(a,b) memset(a,b,sizeof a)
#define pb push_back
#define pii pair<int,int>
using namespace std;
const int mod=1e9+7;
const int maxx=1e6+10;
const double eps=1e-6;
using namespace std;
const long double PI = 3.14159265358979323846;
//inline ll read(){ ll x=0,f=1; char ch=getchar(); while (!isdigit(ch)) { if (ch=='-') f=-1; ch=getchar(); } while (isdigit(ch)) { x=x*10+ch-48;  ch=getchar();  } return x*f;}
//ll cc = ((1ll << 62) - 1 + (1ll << 62));
/*struct node {
    ll l,r ,lazy,val;
    node() :l(),r(),lazy(),val(){}
    node(ll a, ll b, ll c,ll d) :l(a),r(b),lazy(c),val(d){}

};*/
int a[maxx],b[maxx];
map<int,int>mp;
//int sum[maxx];
int get(vector<int> &aa,vector<int> &bb)
{
    int n=aa.size(),m=bb.size();
vector<int>sum(m+20);
   mp.clear();sum.clear();
//cout<<n<<" "<<m<<endl;
    for(int i=0; i<n; i++)
    {
       // cout<<"&&&&&&"<<endl;
        mp[aa[i]]=1;
       // cout<<mp[aa[i]]<<endl;
    }
    for(int i=m-1; i>=0; i--)sum[i]=sum[i+1]+mp[bb[i]];
    int res=sum[0];

    for(int i=0; i<m; i++)
    {
        int up=upper_bound(aa.begin(),aa.end(),bb[i])-aa.begin();
       // cout<<up<<endl;
        int pos=lower_bound(bb.begin(),bb.end(),bb[i]-up+1)-bb.begin();
        //cout<<pos<<endl;
        res=max(res,i-pos+1+sum[i+1]);
        //cout<<res<<endl;
    }
    return res;
}
signed main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n,m;
        cin>>n>>m;
        vector<int>az,af,bz,bf;
        az.clear();af.clear();
        bz.clear();bf.clear();
        for(int i=0; i<n; i++)
        {
            cin>>a[i];
            if(a[i]>0)
                az.push_back(a[i]);
            else
                af.push_back(-a[i]);
        }
        for(int i=0; i<m; i++)
        {
            cin>>b[i];
            if(b[i]>0)
                bz.push_back(b[i]);
            else
                bf.push_back(-b[i]);
        }
        reverse(af.begin(),af.end());
        reverse(bf.begin(),bf.end());
        cout<<(get(az,bz)+get(af,bf))<<endl;
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值