CodeForces 7B
我把我的思路大概写了下注释里面
原因是之前写过这样一个求助
https://www.luogu.com.cn/discuss/show/163636
所以为了省事就直接在上面改了\(QwQ\)
用\(ODT\) (珂朵莉树) 实现唷(^U^)ノ~YO
虽然说没有在学ODT时的基本操作,但确实是区间缩点的思想呢
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <algorithm>
using namespace std;
#define ri register int
//这个是ODT树呢。。
//l,r是这个区间的左右边界,当然都是闭区间
//val是这个区间对应的编号,len是区间的长度
struct ODT
{
int l, r;
mutable int val, len;
ODT(int L = -1, int R = -1, int V = 0, int E = 0)
: l(L), r(R), val(V), len(E) {}
bool operator< (const ODT& x) const
{ return l < x.l;}
};
typedef set<ODT>::iterator IT;
//n,m 如题意;xth是第x个请求成功的内存编号,当然一开始是1啦。。
set<ODT> nc;
int n, m, xth = 1;
char s[13];
//这是尝试申请内存
inline void add_nc(int le)
{
int flag = 1;
IT it = nc.begin();
//找第一个可用的(0)且长度大于等于所需的内存
//并将这段0区间分为两端
for ( ; it != nc.end(); ++it)
{
if (it->val != 0) continue;
if (it->len >= le)
{
int L = it->l, R = it->r, LE = it->len;
int pos = L + le;
nc.erase(it);
nc.insert(ODT(L, pos-1, xth, le));
if (pos <= R) nc.insert(ODT(pos, R, 0, LE-le));
flag = 0;
break;
}
}
//如果没找到输出NULL,否则输出编号
if (flag) printf("NULL\n");
else printf("%d\n", xth), ++xth;
}
//这是删除编号为x的内存
inline void erase_nc(int x)
{
int flag = 1;
IT it = nc.begin();
//寻找这段内存并赋值为零
//(本来可以直接改变找到区间的val为0滴,但不知道为什么比删除后重新加入要慢qwq)
for ( ; it != nc.end(); ++it)
{
if (it->val == x)
{
int L = it->l, R = it->r, LE = it->len;
nc.erase(it);
it = nc.insert(ODT(L, R, 0, LE)).first;
flag = 0;
break;
}
}
//如果不存在编号输出ILLEGAL_ERASE_ARGUMENT
//否则查看左右相邻区间val是否为0,并将其合并
if (flag) printf("ILLEGAL_ERASE_ARGUMENT\n");
else
{
if (it != nc.begin())
{
IT itn = it;
--it;
if (it->val == 0)
{
int L = it->l, R = itn->r, LE = it->len;
LE += itn->len;
nc.erase(it), nc.erase(itn);
itn = nc.insert(ODT(L, R, 0, LE)).first;
}
it = itn;
}
IT itn = it;
++it;
if (it != nc.end() && it->val == 0)
{
int L = itn->l, R = it->r, LE = it->len;
LE += itn->len;
nc.erase(it), nc.erase(itn);
nc.insert(ODT(L, R, 0, LE));
}
}
}
//这是前移操作
inline void defragment()
{
int LEO = 0;
IT it = nc.begin();
//将0区间删除,并累加其长度。
//将非0区间向前移动已经删除的0区间长度之和
for ( ; it != nc.end(); )
{
if(it->val == 0)
{
LEO += it->len;
IT itn = it;
++itn;
nc.erase(it);
it = itn;
} else {
int L = it->l, R = it->r, V = it->val, LE = it->len;
L -= LEO, R -= LEO;
nc.erase(it);
it = nc.insert(ODT(L, R, V, LE)).first;
++it;
}
}
//最后如果还有0区间,将0区间加在最后面
if(LEO) nc.insert(ODT(m-LEO+1, m, 0, LEO));
}
signed main()
{
scanf("%d%d", &n, &m);
//最开始都是0,即全部可使用
nc.insert(ODT(1, m, 0, m));
for (ri i = 1; i <= n; ++i)
{
//靠第一个字母就可以区分了讷。。
scanf("%s", s);
if (s[0] == 'a')
{
int x;
scanf("%d", &x);
add_nc(x);
} else if (s[0] == 'e') {
int x;
scanf("%d", &x);
// 一定要加这个判断!!!
// 这里是我跌倒数次的地方!!
// 这是为了防止一开始就erase时将我们规定的初始无内存占用状态删除
// 所以一旦小于等于0,就直接输出
if (x <= 0) {
printf("ILLEGAL_ERASE_ARGUMENT\n");
continue;
}
erase_nc(x);
} else if (s[0] == 'd') {
defragment();
}
}
return 0;
}
这是经历CSP第一届的残酷打击后我做的第一道题呢\(QwQ\)
最后愿珂朵莉长存~~~
珂朵莉树万岁!!!!
\ \ ""抱歉⋯我已经 绝对不可能再获得幸福了
\ \ \ \ \ \ \ \ \ \ 因为⋯我发现⋯
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 其实我⋯
**\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ 早就已经被幸福包围了"" **