湖南省选 转盘

转盘这道题是我在省选赛场上最接近做出来的一道题。这道题的思想值得总结。

思想一:破环为链

这应该算的上是是一个老套路。具体做法就是开一个2倍的数组,按顺序把要处理的数列存两遍即可

思想二:寻找策略

作为一道省选题,思想性还是很强的。总的来说就是把题意理解透彻。

我们发现,如果某一位置上有一个数x,那么其前面的第一个数如果小于等于x-1是无需考虑的
前面的第二个数如果小于等于x-2是无需考虑的。

以此类推,只要找到那些需要考虑的数再简单比较即可(即以它为起点走一圈)。每一个需要考虑的数都有一个代表的范围。这样我们就发现了一个把题意理解略微透彻的策略

更为透彻一点,即把这种策略转换为数学模型,那么对于下面这列数

3 , 5 , 2 , 3 , 4 , 1 , 9

根据上述策略,只有9与5值得考虑。换句话说,如果把每一个数都转化为他的值减去他的下标的话,即:
2 , 3 , 1 , 1 , 1 , 5 , 2

那么,只要使得后面的数比自己小,那么就可以考虑为需要考虑的数,每一个需要考虑的数能够代表的范围是从自己开始往前走到第一个大于自己的数的后一个位置。

思想三:维护数列

一个需要维护的数列已经出来了。由于需要修改,显然用数据结构去维护它。我们选择什么数据结构?

维护区间,绝对首先考虑线段树

现在考虑怎么用线段树维护。其实,再明确一遍维护目标——找到一段区间,该区间最右边的数大于该区间内所有的数,且该区间不能被包含于其他合法区间 ,这时我们就发现,现在如果有两段区间,我们把该二区间合并,只需用右边区间的最大值在左边区间寻找一个合法端点,这个产生的区间不一定是最优的,但是是可以考虑到最优结果的决策中来的。其它备选的最优决策是什么呢?如果在在左区间二分寻找时,当前枚举的端点右边的区间最大值小于,将没有贡献,如果大于,则在右半段寻找,并且与左半段的最优决策取min。

但这样不禁有一个疑问

每一次的决策实际上只考虑了两段区间左边的最优决策与两段区间新产生的最优决策,对于最右边的区间的原有最优决策没有考虑,这是为什么?
要解决这个问题,可以从宏观,即整体性去想也可以从微观即细节去想。

首先,你要知道这是一个环,那么理论上就只需要考虑后面找前面产生的最值。最后输出这个值,结果显然正确,这个值的推求过程也正确。(ex.比如你输出了整体的这个值,由于你破环为链,开了两倍数组,这时这个值还有没有被考虑的情况吗?没有了,所有区间都考虑了,再加上推导过程合法,正确性保证)。

细节上,你会发现,其实考虑了。因为当有一个在后面的区间时,如果你这个区间太小了自动被吞并,如果足够大,自动被考虑,会在里面二分;

思想四:细节

不多说,上代码

#include<bits/stdc++.h>
#define gc() getchar()
#define LL long long
#define _ 100001
#define __ 200002
using namespace std;
const int inf = 0x3f3f3f;
inline int read(){
    register int data=0;
    register char ch=0;
    while(ch<'0'||ch>'9')ch=gc();
    while(ch<='9'&&ch>='0'){
        data=(data<<3)+(data<<1)+(ch^48);
        ch=gc();
    }
    return data;
}
int n,m,p;
int a[__],val[__<<2],t[__<<2],tl[__<<2];
int L;
int Query(int l,int r,int qp,const int cmp){
    register int mid = (l+r)>>1;
    if(l==r) return val[qp]<=cmp?inf:cmp+l+1;
    //if(l==r) return val[qp]>cmp?cmp+l+1:inf;
    if(val[qp<<1|1]<=cmp)return Query(l,mid,qp<<1,cmp);
    else return min(Query(mid+1,r,qp<<1|1,cmp),tl[qp]);

}
void pushup(register int qp,register int l,register int r){
    val[qp]=max(val[qp<<1],val[qp<<1|1]);
    register int mid= (l+r)>>1;
    t[qp]=min(t[qp<<1|1],tl[qp]=Query(l,mid,qp<<1,val[qp<<1|1]));
    return;
}
void build (int l,int r,int qp){
    if(l==r){val[qp]=a[l]-l,t[p]=inf;return;}
    register int mid = (l+r)>>1;
    build(l,mid,qp<<1);
    build(mid+1,r,qp<<1|1);
    pushup(qp,l,r);
    return;
}
void fix(int l,int r,int qp,int x,int y){
    if(l==r){val[qp]=y-x;t[qp]=inf;return;}
    register int mid =(l+r)>>1;
    if(x<=mid)fix(l,mid,qp<<1,x,y);
    else  fix(mid+1,r,qp<<1|1,x,y);
    pushup(qp,l,r);
    return;
}
int main(){
    n=read();
    m=read();
    p=read();
    L=n<<1;//这里就是在扩展空间
    register int i,aa,bb;
    for(i=1;i<=n;++i)a[i]=read(),a[i+n]=a[i];
    build(1,L,1);
    register int ans;
    printf("%d\n",ans=tl[1]+n-1);
    for(i=1;i<=m;++i){
        aa=read();
        bb=read();
        aa^=(p*ans);
        bb^=(p*ans);
        //a[aa]=bb;
        //a[aa+n]=bb;
        fix(1,L,1,aa,bb);
        fix(1,L,1,aa+n,bb);
        printf("%d\n",ans=tl[1]+n-1);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/jh_2002/article/details/80638813