bzoj1493 [NOI2007]项链工厂(splay)

Description

T公司是一家专门生产彩色珠子项链的公司,其生产的项链设计新颖、款式多样、价格适中,广受青年人的喜爱。
最近T公司打算推出一款项链自助生产系统,使用该系统顾客可以自行设计心目中的美丽项链。该项链自助生产系
统包括硬件系统与软件系统,软件系统与用户进行交互并控制硬件系统,硬件系统接受软件系统的命令生产指定的
项链。该系统的硬件系统已经完成,而软件系统尚未开发,T公司的人找到了正在参加全国信息学竞赛的你,你能
帮助T公司编写一个软件模拟系统吗?一条项链包含 N 个珠子,每个珠子的颜色是 1,2,…,c 中的一种。项链
被固定在一个平板上,平板的某个位置被标记位置 1 ,按顺时针方向其他位置被记为 2,3,…,N。
你将要编写的软件系统应支持如下命令:

Input

输入文件第一行包含两个整数 N,c ,分别表示项链包含的珠子数目以及颜色数目。
第二行包含 N 个整数,x1,x2,…,xn ,表示从位置 1 到位置 N 的珠子的颜色,1≤xi≤c 。
第三行包含一个整数 Q ,表示命令数目。接下来的 Q 行每行一条命令,如上文所述。N≤500000 ,Q≤500000,c≤1000

Output

对于每一个 C 和 CS 命令,应输出一个整数代表相应的答案。

Sample Input

5 3
1 2 3 2 1
4
C
R 2
P 5 5 2
CS 4 1

Sample Output

4
1

HINT

注意旋转命令旋转“珠子”但不改变“位置”的编号,而反转命令始终以位置 1 为对称轴。例如当 N=10 时,项链上的位置编号如图1:



但注意此时项链上的位置编号仍然如图1所示,于是翻转的对称轴不变。因而再执行一次“F”命令时,项链的颜色如图4所示。

关于CountSegment命令
CS命令表示查询一个“线段”中有多少个“部分”。尤其注意当查询的长度等于 N 时,我们仍然将查询部分作为“线段”理解。例如在图4所示的情况中,执行“CS 1 10”命令,查询从位置 1 开始到位置 10 结束的这个长度为 10 的线段中有多少个“部分”,于是得到返回值 3 。与之形成对照的是,若执行“C”命令,返回值则为 2

[ Submit][ Status][ Discuss]





分析:

平衡树
在splay中,我们维护:
cover:区间覆盖标记(区间全部涂成一个颜色)
v:结点颜色
ls:区间最左边结点的颜色
rs:区间最右边结点的颜色
size:区间大小
co:区间内的颜色数目
rev:翻转标记

F 对[2,n]打翻转标记即可

R 将最后k个元素插入到1的前面

S 交换两个点的颜色,把两个点splay到根,更新节点的信息

P 注意染色的时候可能需要分成两段

C 注意判断1,n两个节点的颜色是否相同,相同且颜色数!=1的话颜色段数要-1

CS 可能需要分两段统计答案,和在一起的时候判断1,n的颜色是否相同,相同的话答案-1

tip

写了两节课,交上去T了
加了inline和读优也没有用
后来发现是Rotate的问题(找了一节课啊)
改了改交上去,终于变成WA了。。。

mdzz,调不出来了,只能参考一下学姐的code了

查询区间的时候,为了便于处理,我们一般在两端各加一个新结点

#include<cstdio>
#include<cstring>
#include<iostream>

using namespace std;

const int N=500010;
int n,m,C,top=0,root,a[N];
int ch[N][2],pre[N],v[N],ls[N],rs[N],size[N],cover[N],co[N];
bool rev[N];

void clear(int bh)
{
    ch[bh][0]=ch[bh][1]=size[bh]=co[bh]=pre[bh]=0;
}

inline int get(int bh)
{
    return (ch[pre[bh]][0]==bh? 0:1);
}

inline void update(int bh)
{
    int l=ch[bh][0]; int r=ch[bh][1];
    ls[0]=rs[0]=-2;
    co[bh]=1;
    ls[bh]=(l?ls[l]:v[bh]); rs[bh]=(r?rs[r]:v[bh]);
    size[bh]=size[l]+size[r]+1;
    if (l) co[bh]+=co[l];
    if (r) co[bh]+=co[r];
    if (rs[l]==v[bh]||ls[r]==v[bh]) co[bh]--;
    if (rs[l]==v[bh]&&ls[r]==v[bh]) co[bh]--;
}

void change(int bh)
{
    swap(ch[bh][0],ch[bh][1]);
    swap(ls[bh],rs[bh]);
    rev[bh]^=1;
}

void color(int bh,int c)
{
    v[bh]=ls[bh]=rs[bh]=c;
    cover[bh]=c; co[bh]=1;
}

inline void push(int bh)
{
    if (rev[bh])
    {
        change(ch[bh][0]);
        change(ch[bh][1]);
        rev[bh]=0;
    }
    if (cover[bh])
    {
        color(ch[bh][0],cover[bh]);
        color(ch[bh][1],cover[bh]);
        cover[bh]=0;
    }
}

inline void rotate(int bh)
{
    int fa=pre[bh];
    int grand=pre[fa];
    int wh=get(bh);
    ch[fa][wh]=ch[bh][wh^1];
    pre[ch[fa][wh]]=fa;
    ch[bh][wh^1]=fa;
    pre[fa]=bh;
    pre[bh]=grand;
    if (grand) ch[grand][ch[grand][0]==fa? 0:1]=bh;
    update(fa);
    update(bh);
}

inline void down(int bh) {if (pre[bh]) down(pre[bh]); push(bh);}

inline void splay(int bh,int mb)
{
    down(bh);
    for (int fa;(fa=pre[bh])!=mb;rotate(bh))
        if (pre[fa]!=mb)
            rotate(get(fa)==get(bh)? fa:bh);
    if (!mb) root=bh;
}

inline int build(int l,int r,int fa)
{   
    if (l>r) return 0;
    int now=++top;
    if (l==r)
    {
        v[now]=ls[now]=rs[now]=a[l];
        co[now]=1; size[now]=1; pre[now]=fa; 
        return now;
    }
    int mid=(l+r)>>1;
    v[now]=a[mid];
    ch[now][0]=build(l,mid-1,now);
    ch[now][1]=build(mid+1,r,now); 
    pre[now]=fa; 
    update(now);
    return now;
}

inline int find_pm(int x)   //查找排名为x的结点标号 
{
    int now=root;
    while (1)
    {
        push(now);
        if (ch[now][0]&&size[ch[now][0]]>=x) now=ch[now][0];
        else
        {
            int tmp=(ch[now][0]? size[ch[now][0]]:0);
            tmp++;
            if (tmp>=x) return now;
            now=ch[now][1];
            x-=tmp;
        }
    }
}

inline void reverse(int x)
{
    int f1=find_pm(n-x+1);
    int f2=find_pm(n+2);
    splay(f1,0); splay(f2,f1);
    int now=ch[ch[root][1]][0]; pre[now]=0;
    ch[ch[root][1]][0]=0; 
    update(ch[root][1]); update(root);

    f1=find_pm(1);
    f2=find_pm(2);
    splay(f1,0); splay(f2,f1);
    ch[ch[root][1]][0]=now; pre[now]=ch[root][1];
    update(ch[root][1]); update(root);
}

inline void fan()
{
    int f1=find_pm(2);
    int f2=find_pm(n+2);
    splay(f1,0); splay(f2,f1);
    int now=ch[ch[root][1]][0];
    change(now); 
    update(ch[root][1]);
    update(root);
}

inline void print(int x,int y,int z)
{
    if (x<=y)                      //询问的是一个连续区间 
    {
        int f1=find_pm(x);
        int f2=find_pm(y+2);
        splay(f1,0); splay(f2,f1);
        color(ch[ch[root][1]][0],z);
        update(ch[root][1]); update(root);
    }
    else                           //询问的区间被n号结点分成了两部分 
    {
        int f1=find_pm(1);
        int f2=find_pm(y+2);
        splay(f1,0); splay(f2,f1);
        color(ch[ch[root][1]][0],z);
        update(ch[root][1]); update(root);

        f1=find_pm(x);
        f2=find_pm(n+2);
        splay(f1,0); splay(f2,f1);
        color(ch[ch[root][1]][0],z);
        update(ch[root][1]); update(root); 
    }
}

inline void ask(int x,int y)
{
    if (x<=y)                      //询问的是一个连续区间 
    {
        int f1=find_pm(x);
        int f2=find_pm(y+2);
        splay(f1,0); splay(f2,f1);
        printf("%d\n",co[ch[ch[root][1]][0]]);
    }
    else                           //询问的区间被n号结点分成了两部分 
    {
        int f1=find_pm(1);
        int f2=find_pm(y+2);
        splay(f1,0); splay(f2,f1);
        int ans=co[ch[ch[root][1]][0]];

        f1=find_pm(x);
        f2=find_pm(n+2);
        splay(f1,0); splay(f2,f1);
        ans+=co[ch[ch[root][1]][0]];

        f1=find_pm(2); f2=find_pm(n+1);
        if (v[f1]==v[f2]) ans--;
        printf("%d\n",ans);
    }
}

int main()
{
    scanf("%d%d",&n,&C);
    a[1]=a[n+2]=-1;
    for (int i=2;i<=n+1;i++) scanf("%d",&a[i]);
    root=build(1,n+2,0);
    scanf("%d",&m);
    char s[5]; int x,y,z;
    for (int i=1;i<=m;i++)
    {
        scanf("%s",s);
        if (s[0]=='R')
        {
            scanf("%d",&x);
            reverse(x);
        }
        else if (s[0]=='F') fan();
        else if (s[0]=='S')
        {
            scanf("%d%d",&x,&y);
            int f1=find_pm(x+1);
            int f2=find_pm(y+1);
            swap(v[f1],v[f2]);
            splay(f1,0); splay(f2,0);
        }
        else if (s[0]=='P')
        {
            scanf("%d%d%d",&x,&y,&z);
            print(x,y,z);
        }
        else if (s[1]=='S')
        {
            scanf("%d%d",&x,&y);
            ask(x,y);
        }
        else
        {
            int f1=find_pm(2); 
            int f2=find_pm(n+1);
            int ans=co[root]-2;
            if (ans>1&&v[f1]==v[f2]) ans--;
            printf("%d\n",ans);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值