[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
原文地址:https://www.cnblogs.com/YSFAC/p/11715522.html