- 记住性质才能更好利用: Splay的查询前驱和后继操作
- t r e e . i n s ( v a l ) tree.ins(val) tree.ins(val)插入后会 S p l a y Splay Splay到根节点
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 1e5+10;
int rt,tot,fa[maxn],ch[maxn][2],cnt[maxn],size[maxn];
int val[maxn];
struct Splay {
void maintain(int x) { size[x] = size[ch[x][0]] + size[ch[x][1]] + cnt[x]; }
bool get(int x) { return x == ch[fa[x]][1]; }
void clear(int x) { ch[x][0] = ch[x][1] = fa[x] = val[x] = size[x] = cnt[x] = 0; }
// 向右旋转
void rotate(int x) {
int y = fa[x],z=fa[y],chk=get(x);
ch[y][chk] = ch[x][chk^1];
fa[ch[x][chk^1]] = y;
ch[x][chk^1] = y;
fa[y] = x;
fa[x] = z;
if (z) ch[z][y == ch[z][1]] = x;
maintain(y);
maintain(x);
}
void splay(int x) {
for (int f=fa[x]; f=fa[x],f; rotate(x))
if (fa[f]) rotate(get(x) == get(f) ? f : x);
rt = x;
}
void ins(int k) {
// 树为空 插入到根节点
if (!rt) {
val[++tot] = k;
cnt[tot]++;
rt = tot;
maintain(rt);
return ;
}
int cnr = rt,f = 0;
while (1) {
// 恰好k == 当前值
if (val[cnr] == k) {
cnt[cnr]++;
maintain(cnr);
maintain(f);
splay(cnr);
break;
}
f = cnr;
// 判断去左树还是右树
cnr = ch[cnr][val[cnr] < k];
// 到了叶节点,插入新的节点
if (!cnr) {
val[++tot] = k;
cnt[tot]++;
fa[tot] = f;
ch[f][val[f] < k] = tot;
maintain(tot);
maintain(f);
splay(tot);
break;
}
}
}
// 给一个数k 查它的排名
int rk(int k) {
int res = 0,cnr = rt;
while (1) {
if (k < val[cnr])
cnr = ch[cnr][0];
else {
res += size[ch[cnr][0]];
if (k == val[cnr]) {
splay(cnr);
return res+1;
}
// 加上该节点的值
res += cnt[cnr];
cnr = ch[cnr][1];
}
}
}
// 给出排名k, 查询数
int kth(int k) {
int cnr = rt;
while (1) {
if (ch[cnr][0] && k <= size[ch[cnr][0]])
cnr = ch[cnr][0];
else {
k -= size[ch[cnr][0]] + cnt[cnr];
if (k <= 0) return val[cnr];
cnr = ch[cnr][1];
}
}
}
// 根节点左子树最大值
int pre() {
int cnr = ch[rt][0];
while(ch[cnr][1]) cnr = ch[cnr][1];
return cnr;
}
// 根节点右子树最小值
int nxt() {
int cnr = ch[rt][1];
while(ch[cnr][0]) cnr = ch[cnr][0];
return cnr;
}
// 删除 值为k的节点
void del(int k) {
// 查k的排名将它旋转到根节点
rk(k);
// 多个k, 减去一个然后退出
if (cnt[rt] > 1) {
cnt[rt]--;
maintain(rt);
return ;
}
// 子节点都不存在
if (!ch[rt][0] && !ch[rt][1]) {
clear(rt);
rt = 0;
return ;
}
// 左节点不存在
if (!ch[rt][0]) {
int cnr = rt;
rt = ch[rt][1];
fa[rt] = 0;
clear(cnr);
return ;
}
// 右节点不存在
if (!ch[rt][1]) {
int cnr = rt;
rt = ch[rt][0];
fa[rt] = 0;
clear(cnr);
return ;
}
// 否则将x的左子树最大值旋转上来作为新 ,删除根节点后右子树接上来
int x = pre(),cnr = rt;
splay(x);
// 修改原右子树的父亲节点
fa[ch[cnr][1]] = x;
// 新左子树
ch[x][1] = ch[cnr][1];
clear(cnr);
maintain(rt);
}
} tree;
int n,ans,temp;
int main() {
cin >> n;
for (int i=1; i<=n; ++i) {
cin >> temp;
tree.ins(temp); // 插入后splay到了根节点
if (i==1)
ans += temp;
else {
if (cnt[rt]<=1) {
int left = tree.pre();
int right = tree.nxt();
if (cnt[left] && cnt[right])
ans += min( abs(temp-val[left]) , abs(temp-val[right]) );
else if (cnt[left])
ans += abs(temp-val[left]);
else if (cnt[right])
ans += abs(temp-val[right]);
}
}
}
cout << ans << endl;
}
-
开2个树分别存(人和宠物)
-
同一时间呆在收养所中的,要么全是宠物,要么全是领养者, 所以一个树就可以处理,判断哪一个时刻存转为村宠物或者领养者
- 处理mod
- 处理 可能2子树其中有一个不存在的情况
- 所有值都唯一
套在splay板子上 代码有点长
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int rt,tot,fa[maxn],ch[maxn][2],val[maxn],cnt[maxn],size[maxn];
int mod = 1e6;
struct Splay {
void maintain(int x) { size[x] = size[ch[x][0]] + size[ch[x][1]] + cnt[x]; }
bool get(int x) { return x == ch[fa[x]][1]; }
void clear(int x) { ch[x][0] = ch[x][1] = fa[x] = val[x] = size[x] = cnt[x] = 0; }
// 向右旋转
void rotate(int x) {
int y = fa[x],z=fa[y],chk=get(x);
ch[y][chk] = ch[x][chk^1];
fa[ch[x][chk^1]] = y;
ch[x][chk^1] = y;
fa[y] = x;
fa[x] = z;
if (z) ch[z][y == ch[z][1]] = x;
maintain(y);
maintain(x);
}
void splay(int x) {
for (int f=fa[x]; f=fa[x],f; rotate(x))
if (fa[f]) rotate(get(x) == get(f) ? f : x);
rt = x;
}
void ins(int k) {
// 树为空 插入到根节点
if (!rt) {
val[++tot] = k;
cnt[tot]++;
rt = tot;
maintain(rt);
return ;
}
int cnr = rt,f = 0;
while (1) {
// 恰好k == 当前值
if (val[cnr] == k) {
cnt[cnr]++;
maintain(cnr);
maintain(f);
splay(cnr);
break;
}
f = cnr;
// 判断去左树还是右树
cnr = ch[cnr][val[cnr] < k];
// 到了叶节点,插入新的节点
if (!cnr) {
val[++tot] = k;
cnt[tot]++;
fa[tot] = f;
ch[f][val[f] < k] = tot;
maintain(tot);
maintain(f);
splay(tot);
break;
}
}
}
// 给一个数k 查它的排名
int rk(int k) {
int res = 0,cnr = rt;
while (1) {
if (k < val[cnr])
cnr = ch[cnr][0];
else {
res += size[ch[cnr][0]];
if (k == val[cnr]) {
splay(cnr);
return res+1;
}
// 加上该节点的值
res += cnt[cnr];
cnr = ch[cnr][1];
}
}
}
// 给出排名k, 查询数
int kth(int k) {
int cnr = rt;
while (1) {
if (ch[cnr][0] && k <= size[ch[cnr][0]])
cnr = ch[cnr][0];
else {
k -= size[ch[cnr][0]] + cnt[cnr];
if (k <= 0) return val[cnr];
cnr = ch[cnr][1];
}
}
}
// 根节点左子树最大值
int pre() {
int cnr = ch[rt][0];
while(ch[cnr][1]) cnr = ch[cnr][1];
return cnr;
}
// 根节点右子树最小值
int nxt() {
int cnr = ch[rt][1];
while(ch[cnr][0]) cnr = ch[cnr][0];
return cnr;
}
// 删除 值为k的节点
void del(int k) {
// 查k的排名将它旋转到根节点
rk(k);
// 多个k, 减去一个然后退出
if (cnt[rt] > 1) {
cnt[rt]--;
maintain(rt);
return ;
}
// 子节点都不存在
if (!ch[rt][0] && !ch[rt][1]) {
clear(rt);
rt = 0;
return ;
}
// 左节点不存在
if (!ch[rt][0]) {
int cnr = rt;
rt = ch[rt][1];
fa[rt] = 0;
clear(cnr);
return ;
}
// 右节点不存在
if (!ch[rt][1]) {
int cnr = rt;
rt = ch[rt][0];
fa[rt] = 0;
clear(cnr);
return ;
}
// 否则将x的左子树最大值旋转上来作为新 ,删除根节点后右子树接上来
int x = pre(),cnr = rt;
splay(x);
// 修改原右子树的父亲节点
fa[ch[cnr][1]] = x;
// 新左子树
ch[x][1] = ch[cnr][1];
clear(cnr);
maintain(rt);
}
} tree;
int n,all,hope,k,ans;
int main() {
cin >> n;
while(n--) {
cin >> k >> hope;
if (all == 0)
tree.ins(hope);
if (all > 0) {
if (k==0)
tree.ins(hope);
else {
tree.ins(hope);
int left = tree.pre(),right = tree.nxt();
int lv = val[left],rv = val[right];
if (cnt[left] && cnt[right]) {
if (abs(hope - lv) == abs(hope - rv)) {
ans += abs(hope - lv);
ans = ans % mod;
tree.del(lv);
} else {
int temp = min( abs(hope-lv) , abs(hope-rv) );
ans += temp;
ans = ans % mod;
tree.del( (temp==abs(hope-lv)) ? lv : rv );
}
} else if (cnt[left]) {
//cout << " lv " << lv << endl;
ans += abs(hope - lv);
ans = ans % mod;
tree.del(lv);
} else if (cnt[right]) {
//cout << " rv " << rv << " hope " << hope << " ans " << ans << endl;
ans += abs(hope - rv);
ans = ans % mod;
tree.del(rv);
}
tree.del(hope);
}
}
if (all < 0) { // 顾客树
if (k == 1) // 插入顾客
tree.ins(hope);
else {
tree.ins(hope);
int left = tree.pre(),right = tree.nxt();
int lv = val[left],rv = val[right];
if (cnt[left] && cnt[right]) {
if (abs(hope - lv) == abs(hope - rv)) {
ans += abs(hope - lv);
ans = ans % mod;
tree.del(lv);
} else {
int temp = min( abs(hope-lv) , abs(hope-rv) );
ans += temp;
ans = ans % mod;
tree.del( (temp==abs(hope-lv)) ? lv : rv );
}
} else if (cnt[left]) {
ans += abs(hope - lv);
ans = ans % mod;
tree.del(lv);
} else if (cnt[right]) {
ans += abs(hope - rv);
ans = ans % mod;
tree.del(rv);
}
tree.del(hope);
}
}
all += (k==0 ? 1 : -1);
}
cout << ans << endl;
return 0;
}
- 重写 避免代码复用
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5+10;
int rt,tot,fa[maxn],ch[maxn][2],val[maxn],cnt[maxn],size[maxn];
int mod = 1e6;
struct Splay {
void maintain(int x) { size[x] = size[ch[x][0]] + size[ch[x][1]] + cnt[x]; }
bool get(int x) { return x == ch[fa[x]][1]; }
void clear(int x) { ch[x][0] = ch[x][1] = fa[x] = val[x] = size[x] = cnt[x] = 0; }
// 向右旋转
void rotate(int x) {
int y = fa[x],z=fa[y],chk=get(x);
ch[y][chk] = ch[x][chk^1];
fa[ch[x][chk^1]] = y;
ch[x][chk^1] = y;
fa[y] = x;
fa[x] = z;
if (z) ch[z][y == ch[z][1]] = x;
maintain(y);
maintain(x);
}
void splay(int x) {
for (int f=fa[x]; f=fa[x],f; rotate(x))
if (fa[f]) rotate(get(x) == get(f) ? f : x);
rt = x;
}
void ins(int k) {
// 树为空 插入到根节点
if (!rt) {
val[++tot] = k;
cnt[tot]++;
rt = tot;
maintain(rt);
return ;
}
int cnr = rt,f = 0;
while (1) {
// 恰好k == 当前值
if (val[cnr] == k) {
cnt[cnr]++;
maintain(cnr);
maintain(f);
splay(cnr);
break;
}
f = cnr;
// 判断去左树还是右树
cnr = ch[cnr][val[cnr] < k];
// 到了叶节点,插入新的节点
if (!cnr) {
val[++tot] = k;
cnt[tot]++;
fa[tot] = f;
ch[f][val[f] < k] = tot;
maintain(tot);
maintain(f);
splay(tot);
break;
}
}
}
// 给一个数k 查它的排名
int rk(int k) {
int res = 0,cnr = rt;
while (1) {
if (k < val[cnr])
cnr = ch[cnr][0];
else {
res += size[ch[cnr][0]];
if (k == val[cnr]) {
splay(cnr);
return res+1;
}
// 加上该节点的值
res += cnt[cnr];
cnr = ch[cnr][1];
}
}
}
// 给出排名k, 查询数
int kth(int k) {
int cnr = rt;
while (1) {
if (ch[cnr][0] && k <= size[ch[cnr][0]])
cnr = ch[cnr][0];
else {
k -= size[ch[cnr][0]] + cnt[cnr];
if (k <= 0) return val[cnr];
cnr = ch[cnr][1];
}
}
}
// 根节点左子树最大值
int pre() {
int cnr = ch[rt][0];
while(ch[cnr][1]) cnr = ch[cnr][1];
return cnr;
}
// 根节点右子树最小值
int nxt() {
int cnr = ch[rt][1];
while(ch[cnr][0]) cnr = ch[cnr][0];
return cnr;
}
// 删除 值为k的节点
void del(int k) {
// 查k的排名将它旋转到根节点
rk(k);
// 多个k, 减去一个然后退出
if (cnt[rt] > 1) {
cnt[rt]--;
maintain(rt);
return ;
}
// 子节点都不存在
if (!ch[rt][0] && !ch[rt][1]) {
clear(rt);
rt = 0;
return ;
}
// 左节点不存在
if (!ch[rt][0]) {
int cnr = rt;
rt = ch[rt][1];
fa[rt] = 0;
clear(cnr);
return ;
}
// 右节点不存在
if (!ch[rt][1]) {
int cnr = rt;
rt = ch[rt][0];
fa[rt] = 0;
clear(cnr);
return ;
}
// 否则将x的左子树最大值旋转上来作为新 ,删除根节点后右子树接上来
int x = pre(),cnr = rt;
splay(x);
// 修改原右子树的父亲节点
fa[ch[cnr][1]] = x;
// 新左子树
ch[x][1] = ch[cnr][1];
clear(cnr);
maintain(rt);
}
} tree;
int n,all,hope,k,ans;
int main() {
cin >> n;
while(n--) {
cin >> k >> hope;
if (all == 0)
tree.ins(hope);
bool flag = 0;
if (all>0 && k==0) {
tree.ins(hope);
flag = 1;
}
if (all<0 && k==1) {
tree.ins(hope);
flag = 1;
}
if (flag) {
all += (k==0?1:-1);
continue;
}
tree.ins(hope);
int left = tree.pre(),right = tree.nxt();
int lv = val[left],rv = val[right];
if (cnt[left] && cnt[right]) {
if (abs(hope - lv) == abs(hope - rv)) {
ans = (abs(hope -lv) + ans)%mod;
tree.del(lv);
} else {
int temp = min( abs(hope-lv),abs(hope-rv) );
ans = (ans + temp) % mod;
tree.del((temp==abs(hope-lv))?lv:rv);
}
} else if (cnt[left]) {
ans = (ans + abs(hope - lv)) % mod;
tree.del(lv);
} else if (cnt[right]) {
ans = (ans + abs(hope - rv)) % mod;
tree.del(rv);
}
tree.del(hope);
all += (k==0?1:-1);
}
cout << ans << endl;
return 0;
}
- 一定要锻炼将题目所求转化到学过的东西???目前面临的最大问题,不然白学了 艹