这种签到题场上居然不过,果然是自己的链表水平太差.
题意
维护一个数据结构.要求能进行四种操作:
-
L
L
L 在左边插入一个数字
-
R
R
R 在右边插入一个数字
-
G
G
G 删除一个已经有的数
-
Q
Q
Q 询问在中间的数字,有偶数个的话回答偏右的那个.
保证每次插入的数字是
1
,
2
,
3...
1,2,3...
1,2,3...按顺序的自然数,不会重复. 操作的次数为
1
0
7
10^7
107,后面两个操作的次数不超过
1.5
×
1
0
6
1.5\times 10^6
1.5×106,只有一组数据.
题解
显然不能用带
l
o
g
log
log的数据结构维护. 考虑使用两个
d
e
q
u
e
deque
deque进行维护,保持右边的
s
i
z
e
size
size和左边相等或者比左边多一个,则右边
d
e
q
u
e
deque
deque的最左边那个即为答案. 删除的时候就直接标记,只用两个数表示两边数字的个数可以忽略掉已经删除的数. 我没有用这种维护方法,而是使用了一种自创新数据结构:可查链表. 众所周知链表是可以
O
(
1
)
O(1)
O(1)插入删除但是需要
O
(
n
)
O(n)
O(n)进行查找的一种数据结构. 但是本题中,按顺序插入没有重复的特点保证了我们可以在插入的时候直接标记
x
x
x的位置
i
d
x
id_x
idx?,这样删除的时候直接删除
i
d
x
id_x
idx?,并且动态维护中间数
a
n
s
ans
ans,由于插入和删除操作每次只有一个数,因此中间数每次也最多移动一位,只要用一个
l
e
n
len
len标记当前数据结构中数字的个数即可对这个答案进行维护了,所有的操作复杂度均为
O
(
1
)
O(1)
O(1). 不幸的是,由于我对链表不甚熟练,场上并没有调出来.(比赛结束后删掉四个字符就AC了,当场去世.) 昨天牛客的时候也是拖的假匈牙利板子,不知道自己上辈子造了什么孽. 贴一下自己的渣代码.由于开
2
×
1
0
7
2\times 10^7
2×107长度的数组会导致
M
L
E
MLE
MLE,只能开
1600
万
1600万
1600万,不是非常严谨,但由于本题只有一组数据,可以通过.
const int yuzu=8e6;
typedef int fuko[yuzu<<1|13];
fuko net,pre,id,a;
int main() {
int q,i,cntl=yuzu,len=0,cntr=yuzu+1,now=0,ans=-1;
for (scanf("%d",&q);q--;) {
char op; int x;
auto addl=[&](int x) {
++len,id[x]=cntl;
net[cntl]=~a[cntl+1]?cntl+1:net[cntl];
pre[cntl]=cntl-1;
a[cntl]=x;
if (!~ans) {
ans=cntl--;
return;
}
--cntl;
if (len&1) ans=pre[ans];
};
auto addr=[&](int x) {
++len,id[x]=cntr;
net[cntr]=cntr+1;
pre[cntr]=~a[cntr-1]?cntr-1:pre[cntr];
a[cntr]=x;
if (!~ans) {
ans=cntr++;
return;
}
++cntr;
if (!(len&1)) ans=net[ans];
};
auto del=[&](int x) {
pre[net[x]]=pre[x];
net[pre[x]]=net[x];
a[x]=-1;
--len;
if (!len) {
ans=-1;
return;
}
if (x==ans) {
ans=len&1?pre[ans]:net[ans];
} else if (x<ans) {
!(len&1)?ans=net[ans]:0;
} else {
len&1?ans=pre[ans]:0;
}
};
scanf("\n%c",&op);
if (op=='L') {
addl(++now);
} else if (op=='R') {
addr(++now);
} else if (op=='G') {
scanf("%d",&x);
del(id[x]);
} else {
printf("%d\n",a[ans]);
}
}
}
|