bzoj 3600 没有人的算术 - 替罪羊树 - 线段树
题目都是图片,就不给了,就给链接好了
由于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 没有人的算术 - 替罪羊树 - 线段树的更多相关文章
- 【题解】BZOJ 3600: 没有人的算术——替罪羊树、线段树
题目传送门 题意 具体的自己去上面看吧...反正不是权限题. 简单来说,就是定义了一类新的数,每个数是0或者为 \((x_L, x_R)\) ,同时定义比较大小的方式为:非零数大于零,否则按字典序比较 ...
- bzoj 3600: 没有人的算术
Description Solution 我们可以给每一个数钦定一个权值 , 这样就可以 \(O(1)\) 比较大小了. 考虑怎么确定权值: 用平衡树来维护 , 我们假设根节点管辖 \([1,2^{6 ...
- bzoj 3600 没有人的算术——二叉查找树动态标号
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3600 已知 l 和 r 的排名,想快速知道 k 的排名.那么建一个 BIT ,用已知的排名做 ...
- BZOJ.3307.雨天的尾巴(dsu on tree/线段树合并)
BZOJ 洛谷 \(dsu\ on\ tree\).(线段树合并的做法也挺显然不写了) 如果没写过\(dsu\)可以看这里. 对修改操作做一下差分放到对应点上,就成了求每个点子树内出现次数最多的颜色, ...
- 「BZOJ3065」带插入区间第K小值 替罪羊树×线段树
题目描述 从前有\(n\)只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力\(a_i\).跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间\(k\)小值.他 ...
- 浅谈树套树(线段树套平衡树)&学习笔记
0XFF 前言 *如果本文有不好的地方,请在下方评论区提出,Qiuly感激不尽! 0X1F 这个东西有啥用? 树套树------线段树套平衡树,可以用于解决待修改区间\(K\)大的问题,当然也可以用 ...
- 「BZOJ3600」没有人的算术 替罪羊树+线段树
题目描述 过长--不想发图也不想发文字,所以就发链接吧-- 没有人的算术 题解 \(orz\)神题一枚 我们考虑如果插入的数不是数对,而是普通的数,这就是一道傻题了--直接线段树一顿乱上就可以了. 于 ...
- 【BZOJ3600】没有人的算术 - 替罪羊树+线段树
题意: 题解: Orz vfleaking……真·神题 做法大概是先把题意中定义的“数”都赋一个实数权值,用平衡树来维护整个从大到小排序过的序列,再用线段树查询最值: 这样做为什么是对的?考虑插入一个 ...
- 【BZOJ3600】没有人的算术(替罪羊树+线段树)
点此看题面 大致题意: 定义任意数对\(>0\),数对之间比大小先比第一位.后比第二位,一开始数列全为\(0\),要求你支持\(a_k=(a_x,a_y)\)和询问区间最大值所在位置两种操作. ...
随机推荐
- Django实现电影论坛
主要实现功能: 注册,登陆,发帖,评论帖子,xadmin实现后台提供帖子管理,板块管理等等 首页(搜索,筛选) 发布帖子 帖子详情页(显示帖子具体内容,提供评论) 个人信息页面(显示个人信息,修改个人 ...
- 使用schemasync同步数据库表结构
安装方式 wget http://www.schemasync.org/downloads/SchemaSync-0.9.4.tar.gz tar -xf SchemaSync-0.9.4.tar.g ...
- Frame报文
链路层帧常用的帧格式有两种:Ethernet II 与 IEEE802.3 Ethernet II 格式多用于终端设备的通信 IEEE802.3 格式多用于网络设备的通信 如何区分这两种报文 ...
- git vim 编辑器基本操作
用 git 命令行提交文件时,默认使用 vim 编辑器,基本操作: 按 a, i 或 o 进入编辑模式 按 ESC 进入操作模式 在操作模式下,:wq 为写入退出,:q! 不保存退出
- 虫师的性能测试思想html网页学习
http://www.cnblogs.com/fnng/category/387349.html
- mac版 android studio问题解决
1.mac安装android studio 解决方案:如果你是安装新手,可以下载androud studio boundls 和 安装环境的jdk就可以了,不需要单独在配置环境了,如果你有经验,可以单 ...
- SQL 3
SQL SELECT DISTINCT 语句 SELECT DISTINCT 语句用于返回唯一不同的值. SQL SELECT DISTINCT 语句 在表中,一个列可能会包含多个重复值,有时您也许希 ...
- ubuntu 安装ftp,配置,和java调用
1:安装 ftp服务器 sudo apt-get install vsftpd 自动安装使用的是主机的用户名和密码:liyafei,1367xxx 访问方式,ftp://localhost:21,ft ...
- ConcurrentModificationException
//需求:如何集合中有给定的元素就在集合中在插入一个元素public class ListIteratorDemo2 { public static void main(String[] args) ...
- A + B Problem II(大数加法)
一直格式错误,不想改了,没A #include <iostream> #include <stdio.h> #include <string.h> #include ...