题目:http://acm.hdu.edu.cn/showproblem.php?pid=4699
题目大意:一个文件编译器,有一个指针,五种操作:(1)在指针前面插入一个数(2)把指针前的数删除(3)指针往左移一位(4)指针往右移一位(5)询问前k个数的最大前缀和(其中 k <= n),n 为指针位置,并输出。
思路:以指针为分界线,分别建两个栈,前面为 a,后面为b,由于k<=n,那么对于a 再维护两个值s和s_max,由于b中的元素会加到 a 的栈顶,那么还要在 b 中记录一个 val 值。由于要询问 k ,那么用vector模拟就行了。
I x a.push(x)
D a.pop()
L a.push(b.pop())
R b.push(a.pop())
Q k a[k].s_max
概括起来就是上面这几句。比赛的时候,想太多,我们当时想到链表,天真的以为插入、删除一个数,还要去更新后面的 s和s_max,其实不用,因为 k<=n,只要跟着指针的移动走就好了。。 SB 了,挫啊。。
赛后又问了下别人,他们说他们是用伸展树做的,哎,表示没学过伸展树,后来一看,由于操作简单,这样简单维护下就可以了。。无语。。
没办法,不会就要学,昨天看了一天伸展树,今天过来敲这道题目,调试老半天,调完过样例,来了一发,TLE了。。 由于我之前的L、R都是直接将数旋转的,让树根为指针所在位置,Q的时候,一遍找下去,可能这样比较耗时,后来,拿了 pos 来记录位置,询问时直接旋转k大的数,然后输出,过了样例,一交,发现是 WA,找了老半天错误,然后TLE了。。终于在各种小地方的优化下,以 1968ms 的微弱优势成功AC,虽然时间有点那个啥,但还是兴奋啊,毕竟写了两天了啊,从WA到TLE再到AC。。T^T
总结一下自己犯的各种SB错误:(1)第一遍写,我的 s 表示的是所有前面的和(不是以它为跟的子树),然后又搞了个延时标记,插入删除时对右儿子放一个,然后 s_max 表示的是左边的 s 最大值,上传时只上传 size 。先不说这个s,这个 s_max 就有问题,当前节点的 s_max 是不能由它的左子树过来的(在我这种SB定义下),会小,因为它左儿子可能还有右儿子,也就是说它和它的左儿子不是挨着的。貌似延时标记这样处理这个 s 是对的(当然也只是我个人想想,还缺乏验证)。。(2)感觉到这个 s 有问题,因为我感觉一个节点的每个值,都是相对于以它为根节点的子树来说的,然后我就删掉了这个延时标记,因为这样插入、删除旋转后只影响到树根的维护的值的变化,然后在 push_up(maintain)这里加了 s 和 s_max,然后由于是定义全都换成以它为根的子树了,那么 s_max 也变了,但是第一遍写的时候,还是有一个地方写错了,我写的是 max( ch[ 0 ]->s_max , ch[ 0 ]->s +v ,ch[ 1 ]->s_max ),这里就是一直没发现,今天吃完午饭,然后造随机数据才发现的。。 = = ,ch[ 1 ]->s_max 应该改为 ch[ 1 ]->s_max + v + ch[ 0 ]->s,因为是前缀和,明显要把左子树的和给拿过来嘛,SB了。。 这样改了之后,又交,发现TLE,就感觉别人写的 splay 能过,我的应该也差不多,我把上面求 max 那个分 if 讨论(其实我换 null 的原因就是省的 if 讨论。。囧),这样可能会省一点时间,发现还是TLE,最后我把 init 里的 null 初始化放在 while 外面去了,终于以微弱的领先优势绝杀了。。。 = = (3)改的时候,其实最麻烦的是处理我加上去的那个虚拟节点(因为split 限制 left 不为空),它的 s = 0,s_max = -INF(不能影响到它的父亲结点取s_max),它的左儿子是 null,maintain 的时候要单独考虑,因为null 的 s 为0,不然它的 s_max 就变为0了!
好了,以上纯属我个人娱乐,如果占用大家宝贵时间,实在抱歉。。 = =
哎,看了大家过的时间,Splay应该不至于超时,不知道哪里写搓了。。 希望各路神牛路过的时候能指教一下。。
把两份代码先都贴上来吧。。
栈模拟代码如下:
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int INF = 0x0fffffff ;
struct Node
{
int s,s_max;
int val;
Node(){}
Node(int a,int b,int c) : s(a),s_max(b),val(c) {}
};
vector <Node> a,b;
char str[11];
int main()
{
int q;
while(~scanf("%d",&q))
{
a.clear();
b.clear();
a.push_back(Node(0,-INF,0));
int x;
while(q--)
{
scanf("%s",str);
if(str[0] == 'I')
{
scanf("%d",&x);
int size = a.size();
a.push_back(Node(a.back().s + x,max(a.back().s_max,a.back().s + x),x));
}
else if(str[0] == 'D')
{
if(a.size()>1)
{
a.pop_back();
}
}
else if(str[0] == 'L')
{
if(a.size()>1)
{
int val = a.back().val;
a.pop_back();
b.push_back(Node(0,0,val));
}
}
else if(str[0] == 'R')
{
if(b.size()>=1)
{
int val = b.back().val;
b.pop_back();
a.push_back(Node(a.back().s + val,max(a.back().s_max,a.back().s + val),val));
}
}
else
{
scanf("%d",&x);
printf("%d\n",a[x].s_max);
}
}
}
return 0;
}
伸展树代码如下(1968ms,汗):
#pragma comment(linker, "/STACK:10240000000000,10240000000000")
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int INF = 0x0fffffff ;
struct Node
{
Node* ch[2];
int flag;
int v;
int s_max,s;
int size ;
Node(){}
Node(int v,int flag,Node* null): v(v),flag(flag) { ch[0] = ch[1] = null;s = 0;s_max = 0;size = 1;}
int cmp(int x)
{
if(ch[0]->size+1 == x) return -1;
else return x < ch[0]->size+1 ? 0 : 1;
}
void maintain(Node* null)
{
if(flag == 1 && ch[1] == null)
{
size = 1;
s = 0;
s_max = -INF;
return ;
}
size = 1;
size += ch[0]->size + ch[1]->size ;
s = v;
if(ch[0] != null)
{
s += ch[0]->s;
s_max = max(ch[0]->s_max,ch[0]->s + v);
if(ch[1] != null)
{
s += ch[1]->s;
s_max = max(s_max,ch[0]->s + v + ch[1]->s_max);
}
}
else
{
s_max = v;
if(ch[1] != null)
{
s += ch[1]->s;
s_max = max(v,v + ch[1]->s_max);
}
}
}
};
struct Splay
{
Node *root,*null;
int n,pos;
void init()
{
null = new Node;
null->ch[0] = null->ch[1] = null;
null->v = 0;
null->size = 0;
null->s_max = 0;
null->s = 0;
}
void build()
{
root = new Node(0,1,null);
root->s_max = -INF;
n = pos = 1;
}
void rotate(Node* &o,int d)
{
Node* k = o->ch[d^1];o->ch[d^1] = k->ch[d];k->ch[d] = o;
o->maintain(null);k->maintain(null);o = k;
}
void splay(Node* &o,int k)
{
int d = o->cmp(k);
if(d == 1) k -= o->ch[0]->size + 1;
if(d != -1)
{
Node* p = o->ch[d];
int d2 = p->cmp(k);
if(d2 != -1)
{
int k2 = d2==0? k: k - p->ch[0]->size - 1;
splay(p->ch[d2],k2);
if(d == d2)
rotate(o,d^1);
else rotate(o->ch[d],d);
}
rotate(o,d^1);
}
}
void split(Node* o,int k,Node* &left,Node* &right)
{
splay(o,k);
left = o;
right = o->ch[1];
o->ch[1] = null;
left->maintain(null);
}
Node* merge(Node* left,Node* right)
{
splay(left,left->size);
left->ch[1] = right;
left->maintain(null);
return left;
}
void solve(char str[])
{
int x;
if(str[0] == 'I')
{
n++;
scanf("%d",&x);
Node *mid = new Node(x,0,null);
Node *left,*right,*o;
split(root,pos,left,right);
root = merge(merge(left,mid),right);
pos++;
}
else if(str[0] == 'Q')
{
scanf("%d",&x);
splay(root,x+1);
//debug(root);
printf("%d\n",max(root->ch[0]->s_max,root->ch[0]->s + root->v));
}
else if(str[0] == 'L')
{
if(pos >= 2) pos--;
}
else if(str[0] == 'R')
{
if(pos < n) pos++;
}
else if(pos>1 && str[0] == 'D')
{
n--;
Node *mid,*left,*right,*o;
split(root,pos - 1,left,o);
split(o,1,mid,right);
root = merge(left,right);
pos--;
}
//puts("last");
//debug(root);
}
void debug(Node* o)
{
if(o == null)
{
puts("(null)");
return ;
}
printf("%d|%d|%d(",o->v,o->s_max,o->s);
if(o->ch[0]!=null) printf("%d|%d|%d,",o->ch[0]->v,o->ch[0]->s_max,o->ch[0]->s);
else printf("null,");
if(o->ch[1]!=null) printf("%d|%d|%d",o->ch[1]->v,o->ch[1]->s_max,o->ch[1]->s);
else printf("null");
puts(")");
if(o->ch[0]!=null) debug(o->ch[0]);
if(o->ch[1]!=null) debug(o->ch[1]);
}
} sp;
char str[11];
int main()
{
//reopen("D://in.txt","r",stdin);
//freopen("D://out1.txt","w",stdout);
sp.init();
int n;
while(~scanf("%d",&n))
{
sp.build();
while(n--)
{
scanf("%s",str);
sp.solve(str);
}
}
return 0;
}
/*
11
I -5
I -3
I 9
I -3
Q 4
*/