POJ 3580——SuperMemo(Splay树,经典题)

SuperMemo
Time Limit: 5000MS Memory Limit: 65536K
Total Submissions: 9921 Accepted: 3201
Case Time Limit: 2000MS

Description

Your friend, Jackson is invited to a TV show called SuperMemo in which the participant is told to play a memorizing game. At first, the host tells the participant a sequence of numbers, {A1A2, ...An}. Then the host performs a series of operations and queries on the sequence which consists:

  1. ADD x y D: Add D to each number in sub-sequence {Ax ... Ay}. For example, performing "ADD 2 4 1" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5, 5}
  2. REVERSE x y: reverse the sub-sequence {Ax ... Ay}. For example, performing "REVERSE 2 4" on {1, 2, 3, 4, 5} results in {1, 4, 3, 2, 5}
  3. REVOLVE x y T: rotate sub-sequence {Ax ... AyT times. For example, performing "REVOLVE 2 4 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 2, 5}
  4. INSERT x P: insert P after Ax. For example, performing "INSERT 2 4" on {1, 2, 3, 4, 5} results in {1, 2, 4, 3, 4, 5}
  5. DELETE x: delete Ax. For example, performing "DELETE 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5}
  6. MIN x y: query the participant what is the minimum number in sub-sequence {Ax ... Ay}. For example, the correct answer to "MIN 2 4" on {1, 2, 3, 4, 5} is 2

To make the show more interesting, the participant is granted a chance to turn to someone else that means when Jackson feels difficult in answering a query he may call you for help. You task is to watch the TV show and write a program giving the correct answer to each query in order to assist Jackson whenever he calls.

Input

The first line contains (≤ 100000).

The following n lines describe the sequence.

Then follows M (≤ 100000), the numbers of operations and queries.

The following M lines describe the operations and queries.

Output

For each "MIN" query, output the correct answer.

Sample Input

5
1 
2 
3 
4 
5
2
ADD 2 4 1
MIN 4 5

Sample Output

5

—————————————————————分割线——————————————


题目大意:

对一个序列进行7种操作:

区间操作:
(1):ADD x y D                对一段区间都增加一个值value
(2):REVERSE x y         翻转一个区间
(3):REVOLVE x y T      对一个区间向右移动T步(转化成交换两个相邻的区间)
(4):INSERT x P            插入一段区间
(5):DELETE x               删除一段区间
(6):MIN x y                     返回一段区间的最小值

思路:

对于右移操作就是讲一段区间分割成两半,然后再合并

[x,a][a+1,y],先将a-1旋转到根,再将a+1旋转到根的右儿子,然后把右儿子的左子树割下来,再将y旋转到根,y+1旋转到根的右儿子,最后将被割下来的子树链接到y+1


注意点:

maxn定义成100001会TL!!!

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define Key_value ch[ch[root][1]][0]
#define REP(i,n) for(int i=0;i<(n);++i)
const int INF=1<<30;
const int maxn=100010;
using namespace std;

int root,tot1,ch[maxn][2],key[maxn],Min[maxn],pre[maxn];
int s[maxn],tot2;
int rev[maxn],add[maxn],size[maxn];
int a[maxn];
int n,q;

void update_rev(int rt){
    if(!rt) return ;
    swap(ch[rt][0],ch[rt][1]);
    rev[rt]^=1;
}
void update_add(int rt,int d){
    if(!rt) return ;
    key[rt]+=d;
    add[rt]+=d;
    Min[rt]+=d;
}
void push_up(int rt)
{
    int lson=ch[rt][0],rson=ch[rt][1];
    size[rt]=size[lson]+size[rson]+1;
    Min[rt]=min(key[rt],min(Min[lson],Min[rson]));
}
void push_down(int rt)
{
    int lson=ch[rt][0],rson=ch[rt][1];
    if(add[rt]) {
        update_add(lson,add[rt]);
        update_add(rson,add[rt]);
        add[rt]=0;
    }
    if(rev[rt]) {
        update_rev(lson);
        update_rev(rson);
        rev[rt]=0;
    }
}
void NewNode(int &rt,int f,int k){
    if(tot2) rt=s[tot2--];
    else rt=++tot1;
    ch[rt][0]=ch[rt][1]=0;
    pre[rt]=f;
    key[rt]=k;
    size[rt]=1;
    add[rt]=rev[rt]=0;
    Min[rt]=k;
}
void build(int &rt,int l,int r,int f)
{
    if(l>r) return ;
    int mid=(l+r)>>1;
    NewNode(rt,f,a[mid]);
    build(ch[rt][0],l,mid-1,rt);
    build(ch[rt][1],mid+1,r,rt);
    push_up(rt);
}
void Init()
{
    root=tot1=tot2=0;
    rev[root]=add[root]=ch[root][0]=ch[root][1]=pre[root]=size[root]=0;
    Min[root]=INF;
    NewNode(root,0,-1);
    NewNode(ch[root][1],root,-1);
    REP(i,n) scanf("%d",&a[i]);
    build(Key_value,0,n-1,ch[root][1]);
    push_up(ch[root][1]);
    push_up(root);
}
void Rotate(int x,int kind)
{
    int y=pre[x];
    push_down(y);
    push_down(x);
    ch[y][!kind]=ch[x][kind];
    pre[ch[x][kind]]=y;
    if(pre[y])
        ch[pre[y]][ch[pre[y]][1]==y]=x;
    pre[x]=pre[y];
    pre[y]=x;
    ch[x][kind]=y;
    push_up(y);
}
void Splay(int x,int goal)
{
    push_down(x);
    while(pre[x]!=goal) {
        if(pre[pre[x]]==goal) {
            Rotate(x,ch[pre[x]][0]==x);
        } else {
            int y=pre[x];
            int kind=ch[pre[y]][0]==y;
            if(ch[y][kind]==x) {
                Rotate(x,!kind);
                Rotate(x,kind);
            } else {
                Rotate(y,kind);
                Rotate(x,kind);
            }
        }
    }
    push_up(x);
    if(goal==0) root=x;
}

int Get_kth(int rt,int k)
{
    push_down(rt);
    int z=size[ch[rt][0]]+1;
    if(z==k) return rt;
    if(z>k) return Get_kth(ch[rt][0],k);
    else return Get_kth(ch[rt][1],k-z);
}

void Add(int x,int y,int d)
{
    Splay(Get_kth(root,x),0);
    Splay(Get_kth(root,y+2),root);
    update_add(Key_value,d);
    push_up(ch[root][1]);
    push_up(root);
}
void Reverse(int x,int y)
{
    Splay(Get_kth(root,x),0);
    Splay(Get_kth(root,y+2),root);
    update_rev(Key_value);

}
void Revolve(int x,int y,int k)
{
    if(!k) return ;
    int l=y-x+1;
    l=y-(k%l+l)%l;
    Splay(Get_kth(root,x),0);
    Splay(Get_kth(root,l+2),root);
    int tmp=Key_value;
    Key_value=0;
//    pre[Key_value]=0;
    push_up(ch[root][1]);
    push_up(root);
    Splay(Get_kth(root,y-l+x),0);
    Splay(Get_kth(root,y-l+x+1),root);
    Key_value=tmp;
    pre[Key_value]=ch[root][1];
    push_up(ch[root][1]);
    push_up(root);
}
void Insert(int x,int v)
{
    Splay(Get_kth(root,x+1),0);
    Splay(Get_kth(root,x+2),root);
    NewNode(Key_value,ch[root][1],v);
    push_up(ch[root][1]);
    push_up(root);
}
void erase(int rt)
{
    if(!rt) return ;
    s[++tot2]=rt;
    erase(ch[rt][0]);
    erase(ch[rt][1]);
}
void Delete(int x)
{
    Splay(Get_kth(root,x),0);
    Splay(Get_kth(root,x+2),root);
    erase(Key_value);
    pre[Key_value]=0;
    Key_value=0;
    push_up(ch[root][1]);
    push_up(root);
}
int Get_answer(int x,int y)
{
    Splay(Get_kth(root,x),0);
    Splay(Get_kth(root,y+2),root);
    return Min[Key_value];
}
int main(){
    while(scanf("%d",&n)!=EOF){
        Init();
		scanf("%d",&q);

		char str[10];
		int l,r,k;
		while(q--){
			scanf("%s",str);
			if(!strcmp(str,"ADD")){
				scanf("%d%d%d",&l,&r,&k);
				Add(l,r,k);
			}
			else if(!strcmp(str,"REVERSE")){
				scanf("%d%d",&l,&r);
				Reverse(l,r);
			}
			else if(!strcmp(str,"REVOLVE")){
				scanf("%d%d%d",&l,&r,&k);
				Revolve(l,r,k);
			}
			else if(!strcmp(str,"INSERT")){
				scanf("%d%d",&l,&k);
				Insert(l,k);
			}
			else if(!strcmp(str,"DELETE")){
				scanf("%d",&l);
				Delete(l);
			}
			else{
				scanf("%d%d",&l,&r);
				printf("%d\n",Get_answer(l,r));
			}
		}
	}
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
舍得制作的“语音版星火考研”词库For SuperMemo 2006版,直接新建一个SuperMemo词库,导入后即可使用。该词汇表源自网上流传的一个星火考研词汇版本,共计收入单词3536个,本词库利用外挂的语音库可以实现自动语音功能,具体方法如下: 1.下载外挂语音库,下载地址及更详尽的使用方法请看这里:http://blog.sina.com.cn/s/blog_5f2d67f90100d97d.html 2.在D盘下新建一个“Speech”文件夹,将下载好语音库解压到这个文件夹中。如果你不想放到D盘,请修改“雅思词汇导入.txt"文件,将”file:///d:/“中的”d“替换成你想要的其他盘符。(包括文件夹也可以改,但你得熟悉这些基本的电脑操作)。 3.本压缩包中带有音标字体Ksphonet.TTF。如果音标不能正常显示,需要安装该字体:1)打开控制面板-字体;2)打开菜单"文件->安装新字体";3)选择Ksphonet.TTF存放的文件夹,安装该字体。   附:词库导入方法:   1.先新建一个库,点击“File->New collection”:   2.检查一下文件保存的位置,一般建议放在默认的“SuperMemo2006”的“Systems”文件夹下(当然放到其他地方也没关系)。在弹出的对话框里填上一个好记的文件名,点击“OK”后继续,这样一个空库就建好了。   3.接下来开始导入,点击菜单“File->Import->Q&A txt”。(如果你的“File”菜单里没有“Import”这一项,那你需要到“File->Level”下打开“Professional”这一级别,“Import”菜单项就会出现了。   然后打开刚才解压出来的词库文件(文本格式),在出现的对话框中,默认选项不用修改,点击OK继续。(Tips:如果这时发现选择的文件不对,可以点击对话框中的“choose file to import”按钮返回重选。)   系统开始转换词库,对话框中的“time expected”就是系统预估所需的转换时间,象舍得导入一个500多K大的词库文件,大约需要1分20多秒的样子。   建好后,系统自动返回主界面。然后我们就可以点击“File->Open Collection”,   选择我们刚才建好的词库,点击界面左下角的“Learn ”按钮,开始我们的学习吧。    详尽的词库导入图文教程请打开下面这个链接进行阅览:  http://blog.sina.com.cn/s/blog_5f2d67f90100d07a.html  http://blog.sina.com.cn/learningpower

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值