大部分注释来源
/*
鸣谢:小蒟蒻yyb
《Splay入门解析【保证让你看不懂(滑稽)】》https://blog.csdn.net/qq_30974369/article/details/77587168
*/
#include
using namespace std;
const int MAXN = 100005;
int n,op;
template<class T> void qread(T &sum)
{
sum = 0;
register int sym = 1;
register char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-') sym = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
sum = (sum << 1) + (sum << 3) + ch - '0';
ch = getchar();
}
sum *= sym;
}
template<class T> void qwrite(const T x,int ori)
{
if (x < 0)
{
putchar('-');
qwrite(-x,ori);
}
else
{
if (x >= 10) qwrite(x / 10,ori);
putchar(x % 10 + '0');
}
if (x == ori) putchar('\n');
}
namespace Splay
{
/*
怎么背模板呢?qwq
---------函数----------方法------------------------操作对象
inline pushUp() -------------------------------------节点
inline bool get() //两个短的直接记!-----------------节点
inline rotate() //最基本操作 ------------------------节点
splay() //不停rotate()变成splay() -------------------节点
find() //splay()和find()功能类似 --------------------节点
int Next() //find next好不好记qwq -------------------数字
insert() --------------------------------------------数字
remove() //插入删除是一对 ---------------------------数字
int queryKth() --------------------------------------数字
inline int queryRank() //两个查询是一对qwq ----------数字
//一共10个函数qwq
*/
int son[MAXN][2],fa[MAXN],cnt[MAXN],size[MAXN],val[MAXN];
//爸爸+儿子+两计数(节点个数+重复个数)+记录值
int Root,nid;
inline void pushUp(int x)
{
size[x] = size[son[x][0]] + size[son[x][1]] + cnt[x]; //还要加上自己本身重复的次数。
}
inline bool get(int x)
{
return son[fa[x]][1] == x; //1代表右儿子。如果x是右儿子,恰好返回1.
}
inline void rotate(int x)
{
int y = fa[x],z = fa[fa[x]],which = get(x),w = son[x][which ^ 1];
son[y][which] = w; //X的 X原来在Y的 相对的 那个儿子 变成了 Y原来是X的那个儿子
fa[w] = y;
son[z][get(y)] = x; //x变到原来y的位置。
fa[x] = z;
son[x][which ^ 1] = y; //Y变成了 X原来在Y的 相对的那个儿子
fa[y] = x;
//注意!!!第三句只能放最底下,第二句必须放第三句上面,第一句可以随便放!!!
//因为第三句修改了y是fa[y]的左右儿子。
//记忆:yx zx xy
pushUp(y);
pushUp(x); //注意!!!顺序不能换,因为此时y是x的儿子。要从底向上pushUp.
}
void splay(int x,int aim = 0)
{
while (fa[x] != aim) //一直旋转到x成为goal的儿子(注意是儿子!)
{
int y = fa[x],z = fa[fa[x]]; //注意!!!y和z必须放在循环内,因为每次rotate()后他们都会更新。
if (z != aim)
{
if (get(x) == get(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
if (aim == 0) Root = x; //不过如果aim等于0或不带参,就转到根节点。
}
void find(int x) //如果不存在这个数呢,返回这个数的前驱或者后继节点。
{
int cur = Root;
while (val[cur] != x && son[cur][x > val[cur]])
{
cur = son[cur][x > val[cur]];
}
splay(cur);
/*
从根节点开始,左侧都比他小,右侧都比他大,
所以只需要相应的往左/右递归.
如果当前位置的val已经是要查找的数,
那么直接把他Splay到根节点,方便接下来的操作.
*/
}
int Next(int x,int f) //查找x的前驱(0)或者后继(1).
{
find(x);
if ((val[Root] > x && f) || (val[Root] < x && !f)) return Root;
//假如x存在,则上面的if语句并不会执行。
//假如x不存在,那么它的前驱或后继就是Root。如果我们的查询恰好与x和Root的关系吻合,直接返回Root
//即可。如果相反,按照下面程序也可以找到前驱后继。
int cur = son[Root][f]; //查找后继的话在右儿子上找,前驱在左儿子上找。
while (son[cur][f ^ 1]) cur = son[cur][f ^ 1]; //要反着跳转,否则会越来越大(越来越小)
return cur;
}
void insert(int x)
{
int cur = Root,f = 0; //记录cur的父节点。
while (val[cur] != x && cur)
{
f = cur;
cur = son[cur][x > val[cur]];
}
if (cur) cnt[cur]++;
else
{
cur = ++nid;
if (f) son[f][x > val[f]] = cur; //当f为0时,cur就是根节点。0是虚拟的,令0为根节点的爸爸。
//仅当cur不是根节点时,才有必要建立他与他爸爸之间的关系。
val[cur] = x;
fa[cur] = f;
son[cur][0] = son[cur][1] = 0;
size[cur] = cnt[cur] = 1;
}
splay(cur); //把当前位置移到根,保证结构的平衡.
/*
往Splay中插入一个数,
类似于Find操作,只是如果是已经存在的数,就可以直接在查找
到的节点的进行计数.如果不存在,在递归的查找过程中,
会找到他的父节点的位置,然后就会发现底下没有啦。。。
所以这个时候新建一个节点就可以了.
*/
}
void remove(int x)
{
int last = Next(x,0),nxt = Next(x,1);
splay(last); //find()也是提到根,但find()提的是某个数代表的节点,而splay()提的直接是某个节点。
splay(nxt,last);
int del = son[nxt][0];
if (cnt[del] > 1)
{
cnt[del]--;
splay(del);
}
else son[nxt][0] = 0;
/*
现在就很简单啦,首先找到这个数的前驱,把他Splay到根节点
然后找到这个数后继,把他旋转到前驱的底下,比前驱大的数是后继,
在右子树,比后继小的且比前驱大的有且仅有当前数,在后继的左子树上面,
因此直接把当前根节点的右儿子的左儿子删掉就可以啦qwq
*/
}
int queryKth(int k) //查询第k小的数是多少。(注意取val[]!)
{
int cur = Root;
while (true)
{
if (son[cur][0] && k <= size[son[cur][0]]) cur = son[cur][0];
else if (size[son[cur][0]] + cnt[cur] >= k) return cur;
else
{
k -= size[son[cur][0]] + cnt[cur];
cur = son[cur][1];
}
//这个和主席树差不多了嘛......
}
}
inline int queryRank(int x)
{
find(x);
return size[son[Root][0]]; //如果我们找到了权值为x的节点,那么答案就是他的左子树的大小.
//和下面求kth时不同,由于边框是不算数的(假设有数-inf,1,问第一小的数)
//,1的左子树节点个数就是1,不用加1。
}
}
//若splay()只带一个参,那么是将某节点转到根;
//若带两个参,是将某节点转到目标节点的儿子;
//find()是将某个数代表的节点转到根。
using namespace Splay;
void init()
{
scanf("%d",&n);
insert(0x3f3f3f3f);
insert(-0x3f3f3f3f); //加边框。
}
void work()
{
int x;
for (int i = 1; i <= n; ++i)
{
qread(op), qread(x);
switch (op)
{
case 1 :
{
insert(x);
break;
}
case 2 :
{
remove(x);
break;
}
case 3 :
{
printf("%d\n",queryRank(x));
break;
}
case 4 :
{
printf("%d\n",val[queryKth(x + 1)]); //因为加了一个边框,所以排名要+1。
break;
}
case 5 :
{
printf("%d\n",val[Next(x,0)]);
break;
}
case 6 :
{
printf("%d\n",val[Next(x,1)]);
break;
}
}
}
}
int main()
{
init();
work();
return 0;
}
参见:
rotate()
splay()
queryKth()
queryRank()
#include
using namespace std;
const int MAXN = 50000;
int n,ans;
int turnover[MAXN];
namespace FastIO
{
template<class T> void qread(T &sum)
{
sum = 0;
register int sym = 1;
register char ch = getchar();
while (ch < '0' || ch > '9')
{
if (ch == '-') sym = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9')
{
sum = (sum << 1) + (sum << 3) + ch - '0';
ch = getchar();
}
sum *= sym;
}
template<class T> void qwrite(const T x)
{
if (x < 0)
{
putchar('-');
qwrite(-x);
}
else
{
if (x >= 10) qwrite(x / 10);
putchar(x % 10 + '0');
}
}
}
using namespace FastIO;
namespace Splay
{
int Root, nid;
int son[MAXN][2], fa[MAXN], cnt[MAXN], size[MAXN], value[MAXN];
inline void pushUp(int x)
{
size[x] = size[son[x][0]] + size[son[x][1]] + cnt[x];
}
inline bool get(int x)
{
return son[fa[x]][1] == x;
}
inline void rotate(int x)
{
int y = fa[x], z = fa[y], k = get(x);
son[y][k] = son[x][k ^ 1];
fa[son[x][k ^ 1]] = y;
son[z][get(y)] = x;
fa[x] = z;
son[x][k ^ 1] = y;
fa[y] = x;
pushUp(y);
pushUp(x);
}
void splay(int x,int aim)
{
while (fa[x] != aim)
{
int y = fa[x], z = fa[y];
if (z != aim)
{
if (get(x) == get(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
if (!aim) Root = x;
}
void find(int x)
{
int cur = Root;
while (value[cur] != x && son[cur][x > value[cur]])
cur = son[cur][x > value[cur]];
splay(cur,0);
}
int Next(int x,int f)
{
find(x);
if ((value[Root] > x && f) || (value[Root] < x && !f)) return Root;
int cur = son[Root][f];
while (son[cur][f ^ 1]) cur = son[cur][f ^ 1];
return cur;
}
void insert(int x)
{
int cur = Root, f = 0;
while (cur && value[cur] != x)
{
f = cur;
cur = son[cur][x > value[cur]];
}
if (cur) cnt[cur]++;
else
{
cur = ++nid;
if (f) son[f][x > value[f]] = cur;
fa[cur] = f;
value[cur] = x;
son[cur][0] = son[cur][1] = 0;
size[cur] = cnt[cur] = 1;
}
splay(cur,0);
}
void remove(int x)
{
int pre = Next(x,0), suc = Next(x,1);
splay(pre,0);
splay(suc,pre);
int del = son[suc][0];
if (cnt[del] > 1)
{
cnt[del]--;
splay(del,0);
}
else son[suc][0] = 0;
}
int queryKth(int k)
{
int cur = Root;
while (true)
{
if (size[son[cur][0]] >= k) cur = son[cur][0];
else if (size[son[cur][0]] + cnt[cur] >= k) return cur;
else
{
k -= size[son[cur][0]] + cnt[cur];
cur = son[cur][1];
}
}
}
int queryRank(int x)
{
find(x);
return size[son[Root][0]];
}
inline bool ifgroup(int x)
{
find(x);
if (cnt[Root] > 1) return true;
return false;
}
}
using namespace Splay;
void init()
{
qread(n);
for (int i = 1;i <= n;++i) qread(turnover[i]);
insert(0x3f3f3f3f);
insert(-0x3f3f3f3f);
}
void work()
{
for (int i = 1;i <= n;++i)
{
insert(turnover[i]);
if (i == 1)
{
ans += turnover[i];
continue;
}
if (ifgroup(turnover[i])) continue;
int pre = Next(turnover[i],0);
int suc = Next(turnover[i],1);
ans += min(abs(value[pre] - turnover[i]),abs(value[suc] - turnover[i]));
}
qwrite(ans);
}
int main()
{
init();
work();
return 0;
}
#include
using namespace std;
const int MAXN = 100000, P = 1000000;
int n,ans,pet,man;
int a[MAXN],b[MAXN];
namespace Splay
{
int Root, nid;
int son[MAXN][2], fa[MAXN], cnt[MAXN], size[MAXN], value[MAXN];
inline void pushUp(int x)
{
size[x] = size[son[x][0]] + size[son[x][1]] + cnt[x];
}
inline bool get(int x)
{
return son[fa[x]][1] == x;
}
inline void rotate(int x)
{
int y = fa[x], z = fa[y], k = get(x);
son[y][k] = son[x][k ^ 1];
fa[son[x][k ^ 1]] = y;
son[z][get(y)] = x;
fa[x] = z;
son[x][k ^ 1] = y;
fa[y] = x;
pushUp(y);
pushUp(x);
}
void splay(int x,int aim)
{
while (fa[x] != aim)
{
int y = fa[x], z = fa[y];
if (z != aim)
{
if (get(x) == get(y)) rotate(y);
else rotate(x);
}
rotate(x);
}
if (!aim) Root = x;
}
void find(int x)
{
int cur = Root;
while (value[cur] != x && son[cur][x > value[cur]])
cur = son[cur][x > value[cur]];
splay(cur,0);
}
int Next(int x,int f)
{
find(x);
if ((value[Root] > x && f) || (value[Root] < x && !f)) return Root;
int cur = son[Root][f];
while (son[cur][f ^ 1]) cur = son[cur][f ^ 1];
return cur;
}
void insert(int x)
{
int cur = Root, f = 0;
while (value[cur] != x && cur)
{
f = cur;
cur = son[cur][x > value[cur]];
}
if (cur) cnt[cur]++;
else
{
cur = ++nid;
if (f) son[f][x > value[f]] = cur;
fa[cur] = f;
value[cur] = x;
son[cur][0] = son[cur][1] = 0;
size[cur] = cnt[cur] = 1;
}
splay(cur,0);
}
void remove(int x)
{
int pre = Next(x,0), suc = Next(x,1);
splay(pre,0);
splay(suc,pre);
int del = son[suc][0];
if (cnt[del] > 1)
{
cnt[del]--;
splay(del,0);
}
else son[suc][0] = 0;
}
}
using namespace Splay;
void init()
{
qread(n);
insert(0x3f3f3f3f);
insert(-0x3f3f3f3f);
for (int i = 1;i <= n;++i)
{
qread(a[i]);
qread(b[i]);
}
}
void work()
{
for (int i = 1;i <= n;++i) //一棵Splay既可以放宠物也可以放人qwq
{
if (a[i]) //来人。
{
if (pet) //宠物还有剩余,必须取一个。
{
int target = b[i];
int pre = Next(target,0);
int suc = Next(target,1);
if (abs(value[pre] - target) <= abs(value[suc] - target))
{
ans = (abs(value[pre] - target) % P + ans % P) % P;
remove(value[pre]);
}
else
{
ans = (abs(value[suc] - target) % P + ans % P) % P;
remove(value[suc]);
}
pet--;
}
else //人留下等宠物。
{
insert(b[i]);
man++;
}
}
else //来宠物。
{
if (man) //人还有剩余,必须取一个。
{
int target = b[i];
int pre = Next(target,0);
int suc = Next(target,1);
if (abs(value[pre] - target) <= abs(value[suc] - target))
{
ans = (abs(value[pre] - target) % P + ans % P) % P;
remove(value[pre]);
}
else
{
ans = (abs(value[suc] - target) % P + ans % P) % P;
remove(value[suc]);
}
man--;
}
else //宠物留下等人。
{
insert(b[i]);
pet++;
}
}
}
qwrite(ans % P);
}
int main()
{
init();
work();
return 0;
}