[CODEVS3243]区间翻转(线段树||splay||块链)

题目描述

传送门

题解

splay或者块链的话是裸题,不过好像块链只能过六个点。。。
线段树的话这道题有一个非常关键的数据特点题目里没有说,就是它给出的区间一定是线段树里的整块,那这样的话就可以直接用线段树来搞,区间打标记然后交换左右儿子就可以了。其实和splay也差不多。

代码

线段树

#include
#include
#include
using namespace std;
#define N 150005

int n,m,x,y;
int a[N],tree[N*4],rev[N*4],ch[N*4][2];

inline void build(int now,int l,int r)
{
    int mid=(l+r)>>1;
    if (l==r)
    {
        tree[now]=a[l];
        return;
    }
    ch[now][0]=now<<1,ch[now][1]=now<<1|1;
    build(ch[now][0],l,mid);
    build(ch[now][1],mid+1,r);
}
inline void pushdown(int now,int l,int r,int mid)
{
    if (rev[now])
    {
        swap(ch[now][0],ch[now][1]);
        rev[ch[now][0]]^=1; rev[ch[now][1]]^=1;
        rev[now]=0;
    }
}
inline void reverse(int now,int l,int r,int lrange,int rrange)
{
    int mid=(l+r)>>1;
    if (lrange<=l&&r<=rrange)
    {
        rev[now]^=1;
        return;
    }
    pushdown(now,l,r,mid);
    if (lrange<=mid) reverse(ch[now][0],l,mid,lrange,rrange);
    if (mid+1<=rrange) reverse(ch[now][1],mid+1,r,lrange,rrange);
}
inline void print(int now,int l,int r)
{
    int mid=(l+r)>>1;
    if (l==r)
    {
        printf("%d ",tree[now]);
        return;
    }
    pushdown(now,l,r,mid);
    print(ch[now][0],l,mid);
    print(ch[now][1],mid+1,r);
}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;++i) scanf("%d",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    while (m--)
    {
        scanf("%d%d",&x,&y);
        reverse(1,1,n,x,y);
    }
    print(1,1,n);
}

splay

#include
#include
#include
using namespace std;
#define N 150005
#define INF 2100000000

int n,m,x,y,sz,root;
int a[N],key[N],f[N],ch[N][2],delta[N],size[N];

void update(int x)
{
    size[x]=1;
    if (ch[x][0]) size[x]+=size[ch[x][0]];
    if (ch[x][1]) size[x]+=size[ch[x][1]];
}
int build(int l,int r,int fa)
{
    if (l>r) return 0;
    int mid=(l+r)>>1;
    int now=++sz;
    key[now]=a[mid]; f[now]=fa;
    int lch=build(l,mid-1,now);
    int rch=build(mid+1,r,now);
    ch[now][0]=lch,ch[now][1]=rch;
    update(now);
    return now;
}
int get(int x)
{
    return ch[f[x]][1]==x;
}
void pushdown(int x)
{
    if (x&&delta[x])
    {
        swap(ch[x][0],ch[x][1]);
        delta[ch[x][0]]^=1; delta[ch[x][1]]^=1;
        delta[x]=0;
    }
}
void rotate(int x)
{
    pushdown(f[x]);
    pushdown(x);
    int old=f[x],oldf=f[old],wh=get(x);
    ch[old][wh]=ch[x][wh^1];
    f[ch[old][wh]]=old;
    ch[x][wh^1]=old;
    f[old]=x;
    if (oldf) ch[oldf][ch[oldf][1]==old]=x;
    f[x]=oldf;
    update(old);
    update(x);
}
void splay(int x,int tar)
{
    for (int fa;(fa=f[x])!=tar;rotate(x))
        if (f[fa]!=tar)
            rotate((get(x)==get(fa))?fa:x);
    if (!tar) root=x;
}
int find(int x)
{
    int now=root,fa=0;
    while (1)
    {
        pushdown(now);
        if (x<=size[ch[now][0]])
            now=ch[now][0];
        else
        {
            x-=size[ch[now][0]]+1;
            if (x==0) return now;
            now=ch[now][1];
        }
    }
}
void print(int now)
{
    pushdown(now);
    if (ch[now][0]) print(ch[now][0]);
    if (key[now]!=-INF&&key[now]!=INF)
        printf("%d ",key[now]);
    if (ch[now][1]) print(ch[now][1]);
}
int main()
{
    scanf("%d",&n);
    a[1]=-INF; a[n+2]=INF;
    for (int i=1;i<=n;++i) scanf("%d",&a[i+1]);
    root=build(1,n+2,0);
    scanf("%d",&m);
    while (m--)
    {
        scanf("%d%d",&x,&y);
        int aa=find(x);
        int bb=find(y+2);
        splay(aa,0);
        splay(bb,aa);
        delta[ch[ch[root][1]][0]]^=1;
    }
    print(root);
}

总结

刚开始有一种线段树十分错误的思路,就是不交换左右儿子,只是打标记和下传标记,有标记的话向右儿子走,否则向左走。可当时我没有加一个统计个数的东西,然后导致了毫无逻辑的混乱。

你可能感兴趣的:(题解,线段树,平衡树,块状链表)