HNOI2018D1T2 转盘

题意

可以选定一个初始位置,每一个时间可以往后走一步(是一个圆)或者不走,每一个位置有一个出现物品的时间点,如果当前所在位置的物品已经出现则可以将其拿走,求最少要多久可以拿走所有的物品

题解

首先对于一个固定的起点,一定有一种方法使得往下一个点走的次数恰好为n-1
我们可以先任意选择一个最大值Ti,然后我们可以随便选择一个起点,依次处理每一个位置,这样肯定是最优解之一,如果有一个更优的解绕了若干圈,那么我们把在最大值之前选取的一个当前起点的后缀当做新的起点显然和那个最优解答案是相同的,绕回来之后还是继续要走到最大值所在的位置,然后我们选择一个使得答案最小的起点就可以了
具体的,把原式子倍长,我们有ans=min(max(T[i+j]+n-j-1)),i=1~n,j>=0
考虑简化式子,设a[i]=t[i]-i
ans=min(i+max(A[j]))+n-1,i=1~n,j>=i
那么我们发现可以把a的一个不上升的单调栈搞出来,设其为S,并且设Si的位置是Wi,那么答案就是 min(Wi1+Si)+n1 m i n ( W i − 1 + S i ) + n − 1 ,那么我们需要用线段树来维护这个单调栈
对于每一个位置,我们维护一个fv表示[l,r]其左半部分以及跨越中点的答案的最小值,mi[v]表示整一段的最小值,tree表示整一段a的最大值
然后fv=find(v*2,tree[v*2+1]); find(x,l,r,y)就表示当前这一段值最后加上一个y之后答案的最小值,然后我们直接用一个log来更新fv的值就好了
总复杂度是两个log的

贴代码

#include
#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))

using namespace std;

const int maxn=2e5+5;

int tree[maxn*3],f[maxn*3],mi[maxn*3];
int a[maxn];
int i,j,k,l,m,n,o,x,y,p,an,ans;

void fd(int v,int l,int r,int x){
    if (l==r){
        if (tree[v]>=x) p=l;
    } else{
        int mid=(l+r)/2;
        if (x*2+1]) fd(v*2+1,mid+1,r,x); else fd(v*2,l,mid,x);
    }
}
void find(int v,int l,int r,int x){
    if (l==r){
        if (tree[v]<x) an=min(an,l+x);
    } else{
        int mid=(l+r)/2;
        if (x*2+1]){
            an=min(an,f[v]);
            find(v*2+1,mid+1,r,x);
        } else find(v*2,l,mid,x);
    }
}
void maketree(int v,int l,int r){
    if (l==r){
        tree[v]=a[l]; mi[v]=a[l]+l;
    } else{
        int mid=(l+r)/2;
        maketree(v*2,l,mid); maketree(v*2+1,mid+1,r);
        an=1e6; find(v*2,l,mid,tree[v*2+1]);
        p=l-1;
        fd(v*2,l,mid,tree[v*2+1]);
        f[v]=min(an,p+1+tree[v*2+1]);
        mi[v]=min(f[v],mi[v*2+1]);
        tree[v]=max(tree[v*2],tree[v*2+1]);
    }
}
void change(int v,int l,int r,int x,int y){
    if (l==r){
        tree[v]=y; mi[v]=y+l;
    } else{
        int mid=(l+r)/2;
        if (x<=mid) change(v*2,l,mid,x,y); else change(v*2+1,mid+1,r,x,y);
        an=1e6; find(v*2,l,mid,tree[v*2+1]);
        p=l-1;
        fd(v*2,l,mid,tree[v*2+1]);
        f[v]=min(an,p+1+tree[v*2+1]);
        mi[v]=min(f[v],mi[v*2+1]);
        tree[v]=max(tree[v*2],tree[v*2+1]);
    }
}
int main(){
    freopen("circle.in","r",stdin);
    freopen("circle.out","w",stdout);
    scanf("%d%d%d",&n,&m,&o);
    fo(i,1,n){
        scanf("%d",&a[i]);
        a[i]=a[i]-i; a[i+n]=a[i]-n;
    }
    maketree(1,1,2*n);
    ans=f[1]+n-1;
    printf("%d\n",ans);
    while (m--){
        scanf("%d%d",&x,&y);
        if (o==1){
            x^=ans; y^=ans;
        }
        y=y-x;
        change(1,1,2*n,x,y);
        change(1,1,2*n,x+n,y-n);
        ans=f[1]+n-1;
        printf("%d\n",ans);
    }
    return 0;
}

你可能感兴趣的:(线段树,线段树,单调栈)