题目都是图片,就不给了,就给链接好了

  由于bzoj比较慢,就先给[vjudge传送门]

  有兴趣的可以去逛bzoj[bzoj传送门]

题目大意

  有n个数a[1],a[2],...,a[n],它们开始都是0,现在有两种操作

  1)C l r k,给a[k]赋值为(a[l], a[r])

  2)Q l r,找到a[l], a[l + 1], ..., a[r]中的最大值,并输出它的下标,如果有多个最大值,则输出最小的那一个。

  对于数对的比较,在题目中是这么定义的

  对于任意x, y,若x = 0, y ≠ 0,则有x < y,例如,0 < (0, 0), 0 < (0, (0, 0))

  对于任意数对x, y,若x ≠ 0, y ≠ 0,则先比较第一关键字,再比较第二关键字,因为题目中的数对是递归定义的,所以比较起来可能有点麻烦,例如(0, 0) < (0, (0, (0, 0))), (((0, (0, 0)), (0, 0)), 0) > ((0, 0), (0, 0)), 0)

解题思路

  先想一下暴力吧,暴力想不出来想正解可能有点困难。暴力思路很简单,线段树维护区间最大值,递归比较,虽然会T掉,但是方法可以优化。

  我可以考虑将这些数映射到一个实数区间$[0, 1]$上,让平衡树的每一个节点代表一个实数区间(想想为什么,不然再插一个节点到它下面,这个实数值怎么算?)。进行比较当在生成一个数的时候,我把它插进平衡树里,返回一个它的实数值。这样就可以实现$O(1)$比较。

  至于用哪种平衡树呢,有待思考。我们知道很多平衡树在维护自己的平衡时通常会变得乱糟糟的,因此这个实数区间如果按照原来的实数区间继续做的话会出问题,比如旋转,左端点更小的旋转上来了,这个怎么玩。。一看就会出问题。。所以必须重新计算实数区间。现在作这样规定,若父节点的实数区间为$[vl, vr]$,则它的左子树的实数区间为$[vl, \frac{vl + vr}{2}]$,右子树的实数区间为$[\frac{vl + vr}{2}]$,如果你高兴,可以把$2$改成$3$。

  假设我们用Splay来维护这个映射,当我们插入一个节点或者查询一次岂不是就要重新计算整个树的实数区间吗?也就是说还不如上面的暴力。所以需要重量平衡树,比如替罪羊树。替罪羊树在重构的时候可以顺便把实数区间处理出来。由于重构后对应数对的实数值(就去这个区间的mid吧)也会改变,所以之前的返回实数不可行,只能返回对应的节点,大不了比较的时候多写几行代码。因为替罪羊树深度有保证,所以精度不会炸。

  接着再按返回回来的节点代表的实数值作比较,用线段树维护区间最大值,最大值的下标。再开一个数组记录一下对应位上当前映射到的节点上,这样对于修改操作不会那么麻烦。

  另外,平衡因子取0.75左右比较快。

  (其实这个东西是超现实数,可以建立和实数的一一映射)

Code(超级不简洁的代码)

 /**
* bzoj
* Problem#3600
* Accepted
* Time:8420ms
* Memory:18584k
*/
#include<iostream>
#include<fstream>
#include<cstdio>
#include<ctime>
#include<cctype>
#include<cstring>
#include<cstdlib>
#include<fstream>
#include<sstream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<vector>
#include<stack>
using namespace std;
typedef bool boolean;
#define inf 0xfffffff
#define smin(a, b) a = min(a, b)
#define smax(a, b) a = max(a, b)
template<typename T>
inline void readInteger(T& u){
char x;
int aFlag = ;
while(!isdigit((x = getchar())) && x != '-');
if(x == '-'){
x = getchar();
aFlag = -;
}
for(u = x - ''; isdigit((x = getchar())); u = (u << ) + (u << ) + x - '');
ungetc(x, stdin);
u *= aFlag;
} template<typename T>
class Pair {
public:
T l, r;
Pair(T l = NULL, const T r = NULL):l(l), r(r) { } boolean operator < (Pair b) {
if(l->val != b.l->val) return l->val < b.l->val;
return r->val < b.r->val;
}
}; #define Node ScapegoatTreeNode*
typedef class ScapegoatTreeNode {
private:
const static double factor = 0.73;
public:
double l, r;
double val;
ScapegoatTreeNode* next[];
Pair<Node> p;
int s; ScapegoatTreeNode():l(), r(), val(), s() {
next[] = next[] = NULL;
}
ScapegoatTreeNode(double l, double r, Pair<Node> p):l(l), r(r), val((l + r) / ), s(), p(p) {
next[] = next[] = NULL;
} void maintain() {
s = ;
for(int i = ; i < ; i++)
if(next[i])
s += next[i]->s;
} boolean bad() {
for(int i = ; i < ; i++)
if(next[i] && next[i]->s > (s + ) * factor)
return true;
return false;
}
}ScapegoatTreeNode; typedef class ScapegoatTree {
public:
ScapegoatTreeNode* root;
vector<ScapegoatTreeNode*> lis; ScapegoatTree():root(NULL) { } void travel(ScapegoatTreeNode*& node) {
if(node->next[] != NULL) travel(node->next[]);
lis.push_back(node);
if(node->next[] != NULL) travel(node->next[]);
node->next[] = node->next[] = NULL;
node->s = ;
} ScapegoatTreeNode* rebuild(int l, int r, double vl, double vr) {
if(l > r) return NULL;
int mid = (l + r) >> ;
ScapegoatTreeNode*& node = lis[mid];
double vmid = (vl + vr) / ;
node->l = vl, node->r = vr, node->val = vmid;
node->next[] = rebuild(l, mid - , vl, vmid);
node->next[] = rebuild(mid + , r, vmid, vr);
node->maintain();
return node;
} void remake(ScapegoatTreeNode*& node, ScapegoatTreeNode*& father) {
lis.clear();
travel(node);
int l = , r = lis.size() - ;
ScapegoatTreeNode*& newroot = lis[(l + r) >> ];
rebuild(l, r, node->l, node->r);
if(father != NULL) father->next[(father->next[] == node) ? () : ()] = newroot;
else this->root = newroot;
} ScapegoatTreeNode* insert(ScapegoatTreeNode*& node, double vl, double vr, Pair<Node> p, ScapegoatTreeNode*& bad, ScapegoatTreeNode*& bf) {
double vmid = (vl + vr) / ;
if(node == NULL) {
node = new ScapegoatTreeNode(vl, vr, p);
return node;
}
if(node->p.l->val == p.l->val && node->p.r == p.r) return node;
ScapegoatTreeNode* ret;
if(p < node->p) ret = insert(node->next[], vl, vmid, p, bad, bf);
else ret = insert(node->next[], vmid, vr, p, bad, bf);
if(bad != NULL && bf == NULL) bf = node;
if(node->bad()) bad = node, bf = NULL;
node->maintain();
return ret;
} ScapegoatTreeNode* insert(Pair<Node> p) {
ScapegoatTreeNode* bad = NULL, *fa = NULL, *ret;
ret = insert(root, 0.0, 1.0, p, bad, fa);
if(bad != NULL) remake(bad, fa);
return ret;
}
}ScapegoatTree; ScapegoatTreeNode zero(, , Pair<Node>());
Pair<Node> pzero(&zero, &zero); typedef class SegTreeNode {
public:
Node val;
int maxid;
SegTreeNode* l, *r;
SegTreeNode(Node val = NULL, SegTreeNode* l = NULL, SegTreeNode* r = NULL):val(val), l(l) ,r(r) { } inline void pushUp() {
if(r->val->val < l->val->val)
val = l->val, maxid = l->maxid;
else if(l->val->val < r->val->val) val = r->val, maxid = r->maxid;
else val = l->val, maxid = l->maxid;
}
}SegTreeNode; typedef class SegTree {
public:
SegTreeNode* root; SegTree():root(NULL) { }
SegTree(int size){
build(root, , size);
} void build(SegTreeNode*& node, int l, int r) {
node = new SegTreeNode();
if(l == r) {
node->val = &zero;
node->maxid = l;
return;
}
int mid = (l + r) >> ;
build(node->l, l, mid);
build(node->r, mid + , r);
node->pushUp();
} void update(SegTreeNode*& node, int l, int r, Node val, int index) {
if(l == index && r == index) {
node->val = val;
return;
}
int mid = (l + r) >> ;
if(index <= mid)
update(node->l, l, mid, val, index);
else update(node->r, mid + , r, val, index);
node->pushUp();
} void query(SegTreeNode*& node, int l, int r, int ql, int qr, Node& maxv, int& index) {
if(l == ql && r == qr) {
if(maxv->val < node->val->val) {
maxv = node->val;
index = node->maxid;
} else if(maxv->val == node->val->val)
smin(index, node->maxid);
return;
}
int mid = (l + r) >> ;
if(qr <= mid) query(node->l, l, mid, ql, qr, maxv, index);
else if(ql > mid) query(node->r, mid + , r, ql, qr, maxv, index);
else {
query(node->l, l, mid, ql, mid, maxv, index);
query(node->r, mid + , r, mid + , qr, maxv, index);
}
} int query(int l, int r, int n) {
Node maxv = &zero;
int index = inf;
query(root, , n, l, r, maxv, index);
return index;
}
}SegTree; int n, m;
ScapegoatTree vm;
SegTree st;
Node* lis;
char buf[]; inline void init() {
readInteger(n);
readInteger(m);
st = SegTree(n);
lis = new Node[(const int)(n + )];
for(int i = ; i <= n; i++)
lis[i] = &zero;
} inline void solve() {
for(int i = , a, b, c; i <= m; i++) {
scanf("%s%d%d", buf, &a, &b);
if(buf[] == 'C') {
readInteger(c);
lis[c] = vm.insert(Pair<Node>(lis[a], lis[b]));
st.update(st.root, , n, lis[c], c);
} else {
printf("%d\n", st.query(a, b, n));
}
}
} int main() {
init();
solve();
return ;
}

bzoj 3600 没有人的算术 - 替罪羊树 - 线段树的更多相关文章

  1. 【题解】BZOJ 3600: 没有人的算术——替罪羊树、线段树

    题目传送门 题意 具体的自己去上面看吧...反正不是权限题. 简单来说,就是定义了一类新的数,每个数是0或者为 \((x_L, x_R)\) ,同时定义比较大小的方式为:非零数大于零,否则按字典序比较 ...

  2. bzoj 3600: 没有人的算术

    Description Solution 我们可以给每一个数钦定一个权值 , 这样就可以 \(O(1)\) 比较大小了. 考虑怎么确定权值: 用平衡树来维护 , 我们假设根节点管辖 \([1,2^{6 ...

  3. bzoj 3600 没有人的算术——二叉查找树动态标号

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3600 已知 l 和 r 的排名,想快速知道 k 的排名.那么建一个 BIT ,用已知的排名做 ...

  4. BZOJ.3307.雨天的尾巴(dsu on tree/线段树合并)

    BZOJ 洛谷 \(dsu\ on\ tree\).(线段树合并的做法也挺显然不写了) 如果没写过\(dsu\)可以看这里. 对修改操作做一下差分放到对应点上,就成了求每个点子树内出现次数最多的颜色, ...

  5. 「BZOJ3065」带插入区间第K小值 替罪羊树×线段树

    题目描述 从前有\(n\)只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力\(a_i\).跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间\(k\)小值.他 ...

  6. 浅谈树套树(线段树套平衡树)&学习笔记

    0XFF 前言 *如果本文有不好的地方,请在下方评论区提出,Qiuly感激不尽! 0X1F 这个东西有啥用? 树套树------线段树套平衡树,可以用于解决待修改区间\(K\)大的问题,当然也可以用 ...

  7. 「BZOJ3600」没有人的算术 替罪羊树+线段树

    题目描述 过长--不想发图也不想发文字,所以就发链接吧-- 没有人的算术 题解 \(orz\)神题一枚 我们考虑如果插入的数不是数对,而是普通的数,这就是一道傻题了--直接线段树一顿乱上就可以了. 于 ...

  8. 【BZOJ3600】没有人的算术 - 替罪羊树+线段树

    题意: 题解: Orz vfleaking……真·神题 做法大概是先把题意中定义的“数”都赋一个实数权值,用平衡树来维护整个从大到小排序过的序列,再用线段树查询最值: 这样做为什么是对的?考虑插入一个 ...

  9. 【BZOJ3600】没有人的算术(替罪羊树+线段树)

    点此看题面 大致题意: 定义任意数对\(>0\),数对之间比大小先比第一位.后比第二位,一开始数列全为\(0\),要求你支持\(a_k=(a_x,a_y)\)和询问区间最大值所在位置两种操作. ...

随机推荐

  1. android 控制POS机图文打印(二)

    上一篇文章结束了ESC/POS的指令集,没看过的可以去看一下,可以当作工具文档来使用的 android 控制POS机图文打印(一) 这一篇正式介绍如何使用POS机来打印图文信息. 首先介绍一下,ESC ...

  2. ArcGIS API for javascript开发笔记(五)——GP服务调用之GP模型的发布及使用详解

    感谢一路走来默默陪伴和支持的你~~~ ----------------欢迎来访,拒绝转载---------------- 关于GP模型的制作请点我! 一.GP发布 ArcGIS Desktop可以作为 ...

  3. an upstream response is buffered to a temporary file

    an upstream response is buffered to a temporary file

  4. Python模块:配置文件解析器configparser

    版权声明:本文为博主皮皮http://blog.csdn.net/pipisorry原创文章,未经博主同意不得转载. https://blog.csdn.net/pipisorry/article/d ...

  5. Jedis 对 Redis 的操作详解

    1. JedisUtil2. 键操作3. 字符串操作4. 字节串4. 整数和浮点数5. 列表6. 集合(Set)7. 散列8. 排序sort 本篇主要阐述Jedis对redis的五大类型的操作:字符串 ...

  6. 【weka】分类,cross-validation,数据

    一.分类classifier 如何利用weka里的类对数据集进行分类,要对数据集进行分类,第一步要指定数据集中哪一列做为类别,如果这一步忘记了(事实上经常会忘记)会出现“Class index is ...

  7. python3专业版安装及破解

    1.网址 https://www.jetbrains.com/pycharm/download/#section=windows,打开页面,点击下载专业版 2.这是下载好的文件,双击运行即可. //详 ...

  8. docker命令及操作

    docker pull 镜像名字 dockers images docker image ls docker image rm 镜像名/镜像ID docker ps docker ps -a dock ...

  9. iOS UI基础-4.2应用程序管理 Xib文件使用

    Xib调整使用 1.新建xib文件 New File-->User Interface-->Empty 2.打开新建的xib文件,出现可视化窗口 (1)拖入一个UIView (不是UIVi ...

  10. #C语言初学记录(位运算)

    位运算 Problem Description7-1 数组元素循环右移问题 一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由 ...