[HNOI/AHOI2018]转盘

Luogu4425 , BZOJ5286 , LOJ2495

一些题解,以及暂时看不了的
相关题目:[P4198楼房重建] [[HNOI2017]影魔]

可以发现转化之后你留在原地一定是不优的 , 也就是说你会一直往前走 , 其实区别在于开始的时间
比如原来的最优解为\(a=\{ 1,2,3,6,7 \}\) 可以转化为 \(a=\{3,4,5,6,7\}\)

枚举出发时间\(t\)出发点\(i\) , 枚举等待时间最长的点\(j\) , 则有\[ans=min_i\{max\{T_j-(j-i)\}+n-1\}\]

\(x_i=T_i-i\) , 即\[ans=min_i\{max\{x_j\}+i\}+n-1\]

又有\(x_j>x_{j+n}\) , 所以\(x_{j+n}\)对枚举最大值不会有影响 , 线段树上询问的范围也就可以从\([i,i+n-1]\)换为\([i,2n]\)
线段树维护单调栈结构 , 记\(mx[i] = max\{T_j-j \} , ans[i] = min\{max\{T_j-j\}+i\}\)

非常好的题 , 代码也很值得推敲 , 非常值得借鉴
具体细节见代码
维护单调栈的信息时维护整个区间非常不好搞 , 可以只维护一半的信息 , 另一半通过动态的比较得出
往左边继续找的时候 , 要记得比一下右边的答案 ; 往右边继续找的时候 , 要记得比一下左边的答案 ;
注释更新于\(19.3.31\)

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
    register LL x=0,f=1;register char c=getchar();
    while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
    while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
    return f*x;
}

const int MAXN=2e5+5;

int a[MAXN];
int n,m,type,lastans;

struct SGT{

// ans[i] = min{ max{ Tj-j } + i }  定义为在左儿子中的答案
// mx[i] = max{ Tj-j }
//这里懒得卡什么空间之类的,枚举范围直接到2n,方便想,反正也不会错

    int ans[MAXN<<2],mx[MAXN<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)

    //要求min{max{Xj}+i},所以i要尽可能小,所以维护一半信息的话就应该是把右边的参数传到左边
    //还是当做模板记下来吧...
    inline int query(int rt,int l,int r,int tmp){//得到上一层区间左半边的答案
        //核心:用到tmp或者把tmp继续往下传
        if(l==r) return l+max(mx[rt],tmp);//特判长度为1:必须根据定义
        int mid=(l+r)>>1;
        //直接返回答案也变成了直接传一半的参到另一半
        if(mx[rs]>=tmp) return min(ans[rt],query(rs,mid+1,r,tmp));//右边的mx更大,可能答案在右边,但也要记得比较左边的答案ans[rt]
        else return min(query(ls,l,mid,tmp),tmp+mid+1);//应该往左边找,而右边的答案是tmp+mid+1(已经判过tmp>mx[rs])
    }
    inline void pushup(int rt,int l,int r,int mid){
        //维护最值的写法:直接把一半的参传到另一半去比较,维护某个半边的答案,这样才能做到另半边能够动态的比较得出
        ans[rt]=query(ls,l,mid,mx[rs]);
        mx[rt]=max(mx[ls],mx[rs]);//mx[]很好更新
    }
    inline void build(int rt,int l,int r){
        if(l==r){
            ans[rt]=a[l];
            mx[rt]=a[l]-l;
            return;
        }
        int mid=(l+r)>>1;
        build(ls,l,mid);
        build(rs,mid+1,r);
        pushup(rt,l,r,mid);//非常骚的线段树维护单调栈结构的pushup()
    }
    inline void modify(int rt,int l,int r,int x){
        if(l==r){
            ans[rt]=a[l];
            mx[rt]=a[l]-l;
            return;
        }
        int mid=(l+r)>>1;
        if(x<=mid) modify(ls,l,mid,x);
        else modify(rs,mid+1,r,x);
        pushup(rt,l,r,mid);
    }
#undef ls
#undef rs
}T;

int main(){
    n=read(),m=read(),type=read();
    for(int i=1;i<=n;i++) a[i]=a[i+n]=read();
    T.build(1,1,n<<1);
    printf("%d\n",lastans=T.ans[1]+n-1);
    for(int i=1;i<=m;i++){
        int x=read(),y=read();
        if(type) x^=lastans,y^=lastans;
        a[x]=a[x+n]=y;
        T.modify(1,1,n<<1,x);
        T.modify(1,1,n<<1,x+n);
        printf("%d\n",lastans=T.ans[1]+n-1);
    }
}

\(19.3.21\)注释

转载于:https://www.cnblogs.com/lizehon/p/10574757.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值