1. 程式人生 > >[NOI2003] 文本編輯器 (splay)

[NOI2003] 文本編輯器 (splay)

sca 一個 .com getchar 直接插入 遍歷 插入 logs def

復制炸格式了,就不貼題面了

[NOI2003] 文本編輯器

Solution

對於光標的移動,我們只要記錄一下現在在哪裏就可以了

Insert操作:手動維護中序遍歷結果,即每次取中點像線段樹一樣一樣遞歸建樹,實際操作:把splay中的光標所在位置旋到根,光標後一位旋到根的下面,此時根的右節點的左子樹是空的,直接插入我們所建的樹的根即可

Delete:先找到我們要刪除的是哪一段區間,把光標之前的點旋到根,終點之後的點旋到根的下面,直接扔掉根節點的右兒子的左子樹即可

Get:也是找到區間遞歸中序輸出就可以了

提示:為了防止越界,我在splay插入了兩個換行符,保證題目輸入不會出現就行,因此也要把光標初始值設為1

Code

#include<bits/stdc++.h>
#define il inline
#define rg register
#define lol long long
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)

using namespace std;

const int N=1e6+10;
const int inf=2e9;

void in(int &ans)
{
    ans=0; int f=1; char i=getchar();
    while(i<'0' || i>'9') {if(i=='-') f=-1; i=getchar();}
    while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+i-'0', i=getchar();
    ans*=f;
}

int n,tot,m,now=1,root;
char s[N<<1];

struct Splay {
    int f,size,ch[2];
    Splay(){
        size = ch[0] = ch[1] = 0;
    }
    char val;
}t[N<<1];

il bool get(int x) {
    return t[t[x].f].ch[1]==x;
}

il void up(int x) {
    t[x].size=t[t[x].ch[0]].size+t[t[x].ch[1]].size+1;
}

void rotate(int x) {
    int f=t[x].f,gf=t[f].f;
    int k1=get(x),k2=get(f);
    t[gf].ch[k2]=x,t[x].f=gf;
    t[f].ch[k1]=t[x].ch[k1^1],t[t[x].ch[k1^1]].f=f;
    t[x].ch[k1^1]=f,t[f].f=x;
    up(f); up(x);
}

void splay(int x,int goal) {
    while(t[x].f!=goal) {
        int f=t[x].f,gf=t[f].f;
        if(gf!=goal) get(x)^get(f)?rotate(x):rotate(f);
        rotate(x);
    }
    if(!goal) root=x;
}

int kth(int k) {
    int u=root;
    while(1) {
        int y=t[u].ch[0];
        if(k>t[y].size+1)
            u=t[u].ch[1],k-=t[y].size+1;
        else if(k<=t[y].size) u=y;
        else return u;
    }
}

il void Move() {
    in(now); now++;
}

il int add(char c,int f) {
    t[++tot].val=c; t[tot].size=1;
    t[tot].f=f; return tot;
}

int build(int f,int l,int r,char *s) {
    int mid=l+r>>1;
    char ch=s[mid];
    int u=add(ch,f);//按中序遍歷建樹
    if(l<mid) t[u].ch[0]=build(u,l,mid-1,s);
    if(mid<r) t[u].ch[1]=build(u,mid+1,r,s);
    up(u); return u;
}

void Insert() {
    int x; in(x); strcpy(s,"");
    for(rg int i=1;i<=x;i++) {
        char ch=getchar();
        while(ch==10 || ch==13 || ch<32 || ch>126) ch=getchar();
        s[i]=ch;
    }
    int l=kth(now);
    int r=kth(now+1);
    splay(l,0); splay(r,l);
    t[t[root].ch[1]].ch[0]=build(t[root].ch[1],1,x,s);
    up(t[root].ch[1]); up(root);
}

void Delete() {
    int x; in(x);
    int l=kth(now),r=kth(now+x+1);//r=kth((now+x-1)+1+1)第一個+1指光標指到起點,因為實際上光標指的是起點-1所以要加回來,第二個+1指區間終點的後一個端點
    splay(l,0); splay(r,l);
    int u=t[root].ch[1];
    t[u].ch[0]=0;
    up(u); up(root);
}

void out(int u) {
    if(t[u].ch[0]) out(t[u].ch[0]);
    if(t[u].val!='\n') putchar(t[u].val);
    if(t[u].ch[1]) out(t[u].ch[1]);
}

void Get() {
    int x; in(x);
    int l=kth(now),r=kth(now+x+1);
    splay(l,0); splay(r,l);
    out(t[t[root].ch[1]].ch[0]);
    putchar('\n');
}

int main()
{
    //freopen("data.in", "r", stdin);
    root=add('\n',0),t[root].ch[1]=add('\n',root);up(root);
    in(n); char s[10];
    while(n--) {
        scanf("%s",s);
        if(s[0]=='M') Move();
        if(s[0]=='I') Insert();
        if(s[0]=='D') Delete();
        if(s[0]=='G') Get();
        if(s[0]=='P') now--;
        if(s[0]=='N') now++;
    }
    return 0;
}

博主蒟蒻,隨意轉載.但必須附上原文鏈接

http://www.cnblogs.com/real-l/

[NOI2003] 文本編輯器 (splay)