• [cf 1236 E] Alice and the Unfair Game


    题意:

    给定一个长度为m的序列$A$,你有一个长度为n的棋盘,可以任选一个位置x作为起点。

    在时刻$[1,m+1]$你可以向左或向右移动一格。

    设时刻i你移动后所在的位置为$B_i$,你需要满足对于任意$1leq ileq m$,$A_{i} eq B_{i}$。

    求有多少对有序数对$(x,y)$,使得以x为起点且以y为终点的合法移动路径存在。

    $n,mleq 10^{5},1leq A_{i}leq n$。

    题解:

    首先观察出一个性质:设x向左最远走到l,向右最远走到r,则$[l,r]$中的位置都可以走到。

    为什么呢?我们总可以在不超过m步走到这些位置,然后通过“反复横跳”的操作来躲避$A_{i}$(第m+1步的移动保证了这一点)。

    考虑如何求出右边界$r(x)$,首先我们可以按照题意直接走。复杂度为$O(n^2)$貌似自闭。

    当什么时候我们这一步走不了呢?设当前时间为t,走到了xt。

    那么如果$t$后面的一个$i$能挡住我当且仅当其满足$A_{i}-xt=i-t$。

    移个项,$A_{i}-i=xt-t$,那么将这两项视为两个点$(i,A_{i})$和$(t,xt)$,限制条件就相当于他们在同一条斜率为1的直线上。

    那么现在就有两种做法,一种是维护这些直线然后$O(n)$线性处理,另外一种是用线段树模拟每一步走的操作。

    观察博客的题目可知,我写的是后一种。

    考虑在线段树中维护每个点x的位置$pos_{x}$,问题需要我们每次将所有能走的点往右走一步,也就是pos整体+1。

    运用lazy标记的思想,我们可以每次仅将不能走的点-1,最后把pos整体+m即可。

    如果这样处理的话,那么对于每个限制,(由第一个公式)它影响的就是所有$(pos+lazy)-t=A_{i}-i$的点。

    因为增量lazy和t恰好抵消,于是每个限制影响的就是$pos=A_{i}-i$的点。

    那么我们直接二分找到这样的一段(注意是一段),然后将其整体-1即可。

    最终x的右边界就是$r(x)=seg(x)+m+1$,左边界处理方法类似,但注意移项出来是$A_{i}+i$。

    复杂度$O(nlogn)$。

    代码:

    #include<bits/stdc++.h>
    #define maxn 100005
    #define maxm 500005
    #define inf 0x7fffffff
    #define ll long long
     
    using namespace std;
    ll N,M,A[maxn],B[maxn],tr[maxn<<2];
    ll R[maxn],L[maxn],lz[maxn<<2];
     
    inline ll read(){
        ll x=0,f=1; char c=getchar();
        for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=getchar()) x=x*10+c-'0';
        return x*f;
    }
     
    inline void build(ll l,ll r,ll k){
        if(l==r){tr[k]=l;return;}
        ll mid=l+r>>1;
        build(l,mid,k<<1);
        build(mid+1,r,k<<1|1);
        return;
    }
    inline void add(ll l,ll r,ll x,ll y,ll v,ll k){
        if(x<=l && r<=y){lz[k]+=v;return;}
        ll mid=l+r>>1;
        if(x<=mid) add(l,mid,x,y,v,k<<1);
        if(y>mid) add(mid+1,r,x,y,v,k<<1|1);
        return;
    }
    inline ll query(ll l,ll r,ll p,ll k){
        if(l==r) return tr[k]+lz[k];
        ll mid=l+r>>1;
        if(p<=mid) return query(l,mid,p,k<<1)+lz[k];
        else return query(mid+1,r,p,k<<1|1)+lz[k];
    }
     
    int main(){
        N=read(),M=read();
        for(ll i=1;i<=M;i++) A[i]=read();
        if(N==1){cout<<"0"<<endl;return 0;}
        build(1,N,1);
        for(ll i=1;i<=M;i++){
            ll l=1,r=N,ansl=0,ansr=0;
            while(l<=r){
                ll mid=l+r>>1;
                if(query(1,N,mid,1)<A[i]-i) ansl=mid,l=mid+1;
                else r=mid-1; 
            }
            ansl++,l=1,r=N;
            while(l<=r){
                ll mid=l+r>>1;
                if(query(1,N,mid,1)<=A[i]-i) ansr=mid,l=mid+1;
                else r=mid-1;
            }
            if(ansl<=ansr) add(1,N,ansl,ansr,-1,1);
        }
        for(ll i=1;i<=N;i++) R[i]=min(query(1,N,i,1)+M+1,N);
        memset(tr,0,sizeof(tr));
        memset(lz,0,sizeof(lz));
        build(1,N,1);
        for(ll i=1;i<=M;i++){
            ll l=1,r=N,ansl=0,ansr=0;
            while(l<=r){
                ll mid=l+r>>1;
                if(query(1,N,mid,1)<A[i]+i) ansl=mid,l=mid+1;
                else r=mid-1; 
            }
            ansl++,l=1,r=N;
            while(l<=r){
                ll mid=l+r>>1;
                if(query(1,N,mid,1)<=A[i]+i) ansr=mid,l=mid+1;
                else r=mid-1;
            }
            if(ansl<=ansr) add(1,N,ansl,ansr,1,1);
        }
        for(ll i=1;i<=N;i++) L[i]=max(query(1,N,i,1)-M-1,1ll*1);
        ll ans=0;
        for(ll i=1;i<=N;i++) ans+=R[i]-L[i]+1;
        cout<<ans<<endl;
        return 0;
    }
    E
  • 相关阅读:
    Nginx lingering_close延迟关闭
    Nginx 减少关闭连接的time_wait端口数量
    Tcp协议的keepalive功能
    Nginx 慢启动与拥塞窗口
    Nginx 优化缓冲区与传输效率
    linux 目录分类与文件操作
    Nginx 滑动窗口与缓冲区
    python 关键字yield
    模块与包
    面向对象
  • 原文地址:https://www.cnblogs.com/YSFAC/p/11715522.html
Copyright © 2020-2023  润新知