codeforces 1499C 1D Sokoban(二分+思维)

题目链接
题目大意: 推箱子游戏:你站在0点,有n个箱子,他们分别在 a i a_i ai上,保证0点没箱子,还有m个好位子,你只能向左或向右推箱子,如遇其他箱子,就相当于你推着“连体婴儿”,推两个箱子。问你最多可以箱子在好的位置。


思路:

  • 首先我们知道我们在0点,而且只能向左或者向右推第一个箱子。
  • 其次是我们推箱子的时候可能会出现个一些连续的箱子,后面的则不动,还在原来的位置。
  • 最后我们可以枚举我们当前推到的特殊点也就是好的位置。判断最大值就行了。
    复杂度的话,移动1e5个点,其次二分查找log(n)的,所以是nlog(n)的复杂度。
    我们需要维护一个后面本来就在好位置的,所以需要维护一个后缀和。在推到特殊位置时我要知道我们推了几个箱子,而且要知道推得这么多箱子,最多有多少在指定的好位置上,以为我们推多箱子时是连续的所以我们只需要知道右端点,左端点就是我们枚举的指定位置。
#include 
#include 
#include 
#include
#include
#include
#include 
#include 
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
typedef pair<ll,ll> pii;
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
#define mem(a,x) memset(a,x,sizeof(a))
#define debug(x) cout << #x << ": " << x << endl;
#define rep(i,n) for(int i=0;i<(n);++i)
#define repi(i,a,b) for(int i=int(a);i<=(b);++i)
#define repr(i,b,a) for(int i=int(b);i>=(a);--i)
const int maxn=2e5+10;
#define inf 0x3f3f3f3f
#define sf scanf
#define pf printf
const int mod=1e9+7;
const int MOD=1e9+7;

inline int read() {
     
    int x=0;
    bool t=false;
    char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=true,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return t?-x:x;
}

ll gcd(ll a,ll b) {
     
    while(b) {
     
        ll tmp=a%b;
        a=b;
        b=tmp;
    }
    return a;
}
ll exgcd(ll a,ll b,ll &x,ll &y) {
      //扩欧
    if(b==0) {
     
        x=1,y=0;
        return a;
    }
    ll d=exgcd(b,a%b,y,x);
    y=y-a/b*x;
    return d;
}
ll n,m;
ll a[maxn],b[maxn],d[maxn],c[maxn];
ll cnta, cntb;
ll dis[maxn];
ll sovle(){
     
    if(!cnta||!cntb) return 0;
    ll now=cnta;
    dis[cntb+1]=0;
    for(int i=cntb;i>=1;i--){
     ///维护一个本身就好的后缀
        dis[i]=dis[i+1];///从前一个状态转移过来
        while(now>=1&&d[i]<c[now]) now--;///找到一个小于等于d[i]的位置
        if(now>=1&&d[i]==c[now]) dis[i]++,now--;///原位是好的
    }
    ll p=1,ans=0;
    for(int i=1;i<=cntb;i++){
     
        if(d[i]<c[1]) continue;///这个位置比第一个箱子还小没办法推到
        while(p+1<=cnta&&c[p+1]<=d[i]+p) p++;///连体婴儿 p个箱子
        ll x=upper_bound(d+1,d+1+cntb,d[i]+p-1)-d-1;///找到后一个就是将其个连体婴儿的第一个推到i位//置,
        ans=max(ans,x+1-i+dis[x+1]);///连体婴儿的数量加上本身就是好的数量求个最大值。
    }
    return ans;
}
int main() {
     
    ll t;
    cin>>t;
    while(t--) {
     
        scanf("%lld%lld",&n,&m);
        cnta=cntb=0;
        ll ans=0;
        for(int i=1; i<=n; i++) {
     
            scanf("%lld",&a[i]);
            if(a[i]>=0)
                c[++cnta]=a[i];
        }
        for(int i=1; i<=m; i++) {
     
            scanf("%lld",&b[i]);
            if(b[i]>=0)
                d[++cntb]=b[i];
        }
        ans=sovle();///大于0的数量
        cnta=cntb=0;
        for(int i=n;i>=1;i--){
     
            if(a[i]<0)c[++cnta]=-a[i];
        }
        for(int i=m;i>=1;i--){
     
            if(b[i]<0)d[++cntb]=-b[i];
        }
        ans+=sovle();///小于0的数量
        printf("%lld\n",ans);
    }

    return 0;
}


你可能感兴趣的:(思维,#二分)