【BZOJ】3065: 带插入区间K小值
http://www.lydsy.com/JudgeOnline/problem.php?id=3065
题意:带插入、修改的区间k小值在线查询。(原序列n<=35000, 询问<=175000)
#include <bits/stdc++.h>
using namespace std; const int nTr=1000005, nSg=15000005, alphaA=4, alphaB=5; int STop;
struct Seg *Snull;
struct Seg { Seg *l, *r; int s, cnt; }Sg[nSg], *iSg=Sg, *bin[nSg];
Seg *newS() {
Seg *x;
if(!STop) x=iSg++;
else x=bin[STop--];
x->l=x->r=Snull; x->s=0; x->cnt=1;
return x;
}
void clean(Seg *&x) {
if(x!=Snull && (--x->cnt==0)) {
bin[++STop]=x;
clean(x->l); clean(x->r);
}
x=Snull;
}
Seg *merge(Seg *l, Seg *r) {
if(l==Snull) { ++r->cnt; return r; }
if(r==Snull) { ++l->cnt; return l; }
Seg *p=newS();
p->l=merge(l->l, r->l);
p->r=merge(l->r, r->r);
p->s=l->s+r->s;
return p;
}
Seg *ins(Seg *p, int k, int _s, int l, int r) {
if(p->s+_s==0) { ++Snull->cnt; return Snull; }
int mid=(l+r)>>1;
Seg *x=newS();
x->l=p->l; ++x->l->cnt;
x->r=p->r; ++x->r->cnt;
x->s=p->s+_s;
if(l==r) return x;
if(k<=mid) { --x->l->cnt; x->l=ins(p->l, k, _s, l, mid); }
else { --x->r->cnt; x->r=ins(p->r, k, _s, mid+1, r); }
return x;
}
Seg *ins(Seg *p, int k, int s) {
Seg *x=ins(p, k, s, 0, 70000);
clean(p);
return x;
}
struct node *null;
struct node {
node *c[2];
int s, k;
Seg *t;
void pushup() { s=c[0]->s+c[1]->s+1; t=ins(merge(c[0]->t, c[1]->t), k, 1); }
}Tr[nTr], *iTr=Tr, *root;
node* newT(int k) {
node *x=iTr++;
x->s=1; x->k=k; x->c[0]=x->c[1]=null; x->t=Snull; ++Snull->cnt;
return x;
}
node* flatten(node *x, node *y) {
if(x==null) return y;
clean(x->t);
x->c[1]=flatten(x->c[1], y);
return flatten(x->c[0], x);
}
node* build(node *x, int n) {
if(n==0) { x->c[0]=null; return x; }
node *y=build(x, n>>1);
node *z=build(y->c[1], n-(n>>1)-1);
y->c[1]=z->c[0];
z->c[0]=y;
y->pushup();
return z;
}
void rebuild(node *&x) {
static node y;
node *head=flatten(x, &y);
build(head, x->s);
x=y.c[0];
}
node *rB;
void insert(node *&x, int pos, int val) {
if(x==null) { x=newT(val); x->t=ins(Snull, val, 1); return; }
++x->s;
x->t=ins(x->t, val, 1);
int s=x->c[0]->s;
if(pos<=s) insert(x->c[0], pos, val);
else insert(x->c[1], pos-s-1, val);
if(max(x->c[0]->s, x->c[1]->s)*alphaB<=x->s*alphaA) { if(rB!=null) rebuild(x->c[rB==x->c[1]]), rB=null; }
else rB=x;
}
int update(node *x, int pos, int val) {
x->t=ins(x->t, val, 1);
int s=x->c[0]->s+1;
if(pos==s) swap(val, x->k);
else if(pos<s) val=update(x->c[0], pos, val);
else val=update(x->c[1], pos-s, val);
x->t=ins(x->t, val, -1);
return val;
}
struct Link {
int len;
struct Ln { int l, r; Seg *t; }val[nTr<<2];
void add(Seg *x) { if(x==Snull) return; ++len; val[len].t=x; val[len].l=len-1; val[len].r=0; val[len-1].r=len; }
void del(int x) { val[val[x].l].r=val[x].r; val[val[x].r].l=val[x].l; }
void clr() { len=0; val[0].l=val[0].r=0; }
};
void query(node *x, int R, Link &a, Link &b) {
if(x==null || !R) return;
int s=x->c[0]->s+1;
if(s<=R) a.add(x->t), b.add(x->c[1]->t), query(x->c[1], R-s, a, b);
else query(x->c[0], R, a, b);
}
int last;
Link a, b;
void Query(int x, int y, int k) {
a.clr(); b.clr();
query(root, x-1, b, a);
query(root, y, a, b);
int l=0, r=70000;
while(l<r) {
x=a.val[0].r, y=b.val[0].r;
int sum=0;
while(x) sum+=a.val[x].t->l->s, x=a.val[x].r;
while(y) sum-=b.val[y].t->l->s, y=b.val[y].r;
x=a.val[0].r, y=b.val[0].r;
if(k<=sum) {
r=(l+r)>>1;
while(x) { a.val[x].t=a.val[x].t->l; if(a.val[x].t==Snull) a.del(x); x=a.val[x].r; }
while(y) { b.val[y].t=b.val[y].t->l; if(b.val[y].t==Snull) b.del(y); y=b.val[y].r; }
}
else {
l=((l+r)>>1)+1;
k-=sum;
while(x) { a.val[x].t=a.val[x].t->r; if(a.val[x].t==Snull) a.del(x); x=a.val[x].r; }
while(y) { b.val[y].t=b.val[y].t->r; if(b.val[y].t==Snull) b.del(y); y=b.val[y].r; }
}
}
printf("%d\n", last=l);
}
void Update(int x, int val) {
update(root, x, val);
}
void Insert(int x, int val) {
rB=null;
insert(root, x, val);
if(rB!=null) rebuild(root);
}
void build(node *&x, int l, int r) {
if(l>r) return;
int mid=(l+r)>>1;
x=newT(-1);
build(x->c[0], l, mid-1);
scanf("%d", &x->k);
build(x->c[1], mid+1, r);
x->pushup();
}
void init() {
Snull=new Seg; Snull->l=Snull->r=Snull; Snull->s=0;
null=new node; null->s=null->k=0; null->t=Snull; null->c[0]=null->c[1]=null;
root=null;
}
int main() {
init();
int n, x, y, k; scanf("%d", &n);
build(root, 1, n);
scanf("%d", &n);
char s[5];
while(n--) {
scanf("%s", s); scanf("%d%d", &x, &y); x^=last, y^=last;
if(s[0]=='Q') scanf("%d", &k), k^=last, Query(x, y, k);
else if(s[0]=='M') Update(x, y);
else Insert(x-1, y);
}
return 0;
}
吐槽:妈呀,这题我写了12个小时.................................................................................................................................................................还是期末考试后10天内A的第一道题...我是有多颓废....
afo的节奏啊.............................................................
题解:
本题我自己yy出一种做法,线段树套splay...然后发现vfk的blog已经有这种做法...而且还说明会tle.............................所以果断没写了.....大概就是线段树维护权值,splay维护区间,插入的时候直接从线段树根走到叶子均插入区间到splay中,然后将原序列后边的值在splay里打个tag表示向后移一步...(或者还有更简单的?),修改就是先删除后插入,查询在线段树二分权值即可....
然后vfk写了好多份题解QAQ...我只是为了学替罪羊好去a掉紫荆花之恋这题..没想到颓废这一题就颓了12h...........................
原因在哪..............RERERERERERERERERERERERE............函数式线段树写跪了..............然后大概调了10小时........(第一次写引用计数啊...而且出了好多奇葩问题...)跪烂那些时间排名靠前的做法orzzzzzzzzzzzz
替罪羊树:详细看vfk论文...我就说说大概...............
插入:如果子树的size>当前根的size*alpha,那么就暴力重建 = =...可以证明复杂度均摊O(lgn)....(在论文里还有个深度平衡...其实没必要= =...学习hzwer神犇的姿势...我们只需要找到第一个大小平衡的根然后找到对应子女是大小不平衡的进行重建即可...
删除:bst一样........(好久没写过的样子..........似乎也是当子树那啥然后暴力重建....
无旋转....
所有操作复杂度都是O(lgn)的..
这里有一种很神的重建技巧...使其空间保持在O(lgn)内..(可能常数大了不少 = =)
这个操作叫拍扁(排便)23333333333333333333333
先将子树转化为链表...(炒鸡简单...定义flatten(x,y)返回x即其子树最前边的那个,且最后边后边加入一个y,则
if(x==null) return y; right(x)=flatten(right(x), y); return flatten(left(x), x);
是不是炒鸡容易理解...(递归定义啊亲..
然后是重建...(也是炒鸡简单,只不过绕了一下..定义build(x,n)表示返回当前子树的假根(是x开头长度为n的链表最后一个元素的后一个元素,不是要求的根),子树x都在这个假根的左子女中,且左子女大小为n
那么
if(n==0) { left(x)=null; return x; }
node *y=build(x, n/2);
node *z=build(right(y), n-n/2-1);
right(y)=left(z): //后边序列为右子树,其实真正得到的根是y而不是z,而由定义知道z是后边序列的假根,所以假根的左子女包含了所有的原列表的元素
left(z)=y; //定义得出
return z;
然后这就是拍扁....
最后是插入的一个技巧,请看代码....
然后进入正题..(妈呀,果然垃圾回收是正题吗...
首先膜拜hzwer的垃圾回收,因为我没看懂QAQ,为什么在函数式插入那里竟然直接修改了!!!(@hzwer
然后膜拜vfk的垃圾回收,恩.我选择的就是这种..............引用计数.................
引用计数..由名字可以看出是由引用决定了当前元素是否存在的.....
那么我们维护一个cnt值,表示当前元素被多少所有存在的元素引用(注意一定是存在的元素,即不是引用,例如x是元素,node *y=x,y只能算一种引用而不是元素,其实可以直接理解为,每个元素的l和r指向的元素就为调用1次)
真正的元素一定是先new了一个节点,然后更改的那个。
因此我们发现每一次new的时候一定是被别的元素给引用过了...所以cnt=1
每一次插入的时候,如果最终的size=0,说明这个元素已经无用...给null的cnt+1,返回null(即表示用ins赋值的那个元素引用了null
然后其实我们ins递归定义是返回一个被引用的指针,即cnt是要被++的
随便搞搞就行辣...
接下来是两棵函数式线段树合并...递归定义merge(a,b)返回一棵合并了a和b的新元素(或者是其中之一的引用)
首先如果有一棵是null,那么直接返回另一棵的元素,即引用,所以cnt要++
而如果都不是null,由于我们是函数式...从不修改任何东西..于是我们就new一个元素....然后大小为两棵线段树的大小之和,然后递归定义我们左右子女的引用....
然后就好辣...
最后是垃圾回收....如果cnt为0的时候就要去掉....
唔?你问我什么时候调用回收?当然是一个新的元素的引用少了一个时调用啦...比如说t=ins(t, a, b); 其实t原来指向的元素少了t这个东西引用,,,因此要这样p=t; t=ins(p, a, b); clean(p);因为t没有引用原来引用的东西辣....
然后写完后喜闻乐见的时间垫底了...............
【BZOJ】3065: 带插入区间K小值的更多相关文章
- bzoj 3065: 带插入区间K小值 替罪羊树 && AC300
3065: 带插入区间K小值 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 1062 Solved: 253[Submit][Status] Des ...
- 【题解】BZOJ 3065: 带插入区间K小值——替罪羊树套线段树
题目传送门 题解 orz vfk的题解 3065: 带插入区间K小值 系列题解 一 二 三 四 惨 一开始用了一种空间常数很大的方法,每次重构的时候merge两颗线段树,然后无限RE(其实是MLE). ...
- BZOJ 3065 带插入区间K小值(sag套线段树)
3065: 带插入区间K小值 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 4696 Solved: 1527[Submit][Status][Di ...
- bzoj 3065: 带插入区间K小值(分块)
Description 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它 ...
- BZOJ 3065 带插入区间K小值
http://www.lydsy.com/JudgeOnline/problem.php?id=3065 思路:替罪羊树套权值线段树. 当替罪羊树某个子树大于某个比利(比例)时就暴力重构,本题时间复杂 ...
- BZOJ 3065 带插入区间K小值 (替罪羊树套线段树)
毒瘤题.参考抄自博客:hzwer 第一次写替罪羊树,完全是照着题解写的,发现这玩意儿好强啊,不用旋转每次都重构还能nlognnlognnlogn. 还有外面二分和里面线段树的值域一样,那么r = mi ...
- 3065: 带插入区间K小值_树套树_替罪羊树_权值线段树
经过周六一天,周一3个小时的晚自习,周二2个小时的疯狂debug,终于凭借自己切掉了这道树套树题. Code: #include <cstdio> #include <algorit ...
- 【学习笔记】浅析平衡树套线段树 & 带插入区间K小值
常见的树套树 一般来说,在嵌套数据结构中,线段树多被作为外层结构使用. 但线段树毕竟是 静态 的结构,导致了一些不便. 下面是一个难以维护的例子: 带插入区间 \(k\) 小值问题 来源:Luogu ...
- [BZOJ3065]带插入区间K小值 解题报告 替罪羊树+值域线段树
刚了一天的题终于切掉了,数据结构题的代码真**难调,这是我做过的第一道树套树题,做完后感觉对树套树都有阴影了......下面写一下做题记录. Portal Gun:[BZOJ3065]带插入区间k小值 ...
随机推荐
- 重温WCF之WCF抛出异常的处理SOAP Fault(十二)
1.(服务端)抛出和(客户端)捕获SOAP Fault 当我们需要客户端获取到WCF服务端的抛出的异常的时候,使用FaultException类 WCF类库在System.ServiceModel命名 ...
- protobuf-net 对象二进制序列化与反序列号(转)
概述: Protobuf是google开源的一个项目,用户数据序列化反序列化,google声称google的数据通信都是用该序列化方法.它比xml格式要少的多,甚至比二进制数据格式也小的多. Prot ...
- AngularJS讲义 - 作用域
什么是作用域? Angular中作用域(scope)是模板以及工作的上下文环境,作用域中存放了应用模型和视图相关的回调行为.作用域是层次化结构的与相关联的DOM结构相对应.作用域可以观察表达式以及传播 ...
- PHPCMS V9 WAP手机门户域名绑定
如需要绑定域名为wap.domain.com,作下如操作: 一.把wap.domain.com域名绑定到你的这个网站主机上. 二.在网站后台模块>手机门户域名里面填写“http://wap.do ...
- hdu 4050 2011北京赛区网络赛K 概率dp ***
题目:给出1-n连续的方格,从0开始,每一个格子有4个状态,左右脚交替,向右跳,而且每一步的步长必须在给定的区间之内.当跳出n个格子或者没有格子可以跳的时候就结束了,求出游戏的期望步数 0:表示不能到 ...
- [Outlook]设置邮件自动接收时间
[Outlook]设置邮件自动接收时间 找了好久,一直都没设置正常,导致老是收到邮件有延迟,今天头脑清晰,搜了一下,然后自己竟然给找到了,记下来当笔记,好记性不如烂笔头,呵呵 搜索百度&quo ...
- java Integer和int的拆箱与装箱
官网:http://docs.oracle.com/javase/tutorial/java/data/autoboxing.html 1.赋值: a. 把int类型赋值给Integer类型:JVM会 ...
- 机器学习公开课笔记(5):神经网络(Neural Network)——学习
这一章可能是Andrew Ng讲得最不清楚的一章,为什么这么说呢?这一章主要讲后向传播(Backpropagration, BP)算法,Ng花了一大半的时间在讲如何计算误差项$\delta$,如何计算 ...
- ZOJ 3494 BCD Code(AC自动机+数位DP)
BCD Code Time Limit: 5 Seconds Memory Limit: 65536 KB Binary-coded decimal (BCD) is an encoding ...
- Codeforces Round #14 D. Two Paths(求树上两条不相交的路径的乘积最大值)
题目链接: http://codeforces.com/problemset/problem/14/D 思路:直接枚举每一天路径的两端,然后求以每一端为树根的树上最长路径,然后相乘就可以了. # ...