写在前面
“模板库”这一系列文章用来复习
模板
由于时间原因,作者无法一一亲自调试其中的程序,也因如此,有一部分程序来自于互联网,如果您觉得这侵犯了您的合法权益,请联系
删除。
对于给您造成的不便和困扰,我表示深深的歉意。
本系列文章仅用于学习,禁止任何人或组织用于商业用途。
本系列文章中,标记*的为选学算法,在
中较少涉及。
数据结构
队列
【简介】
队列是一种特殊的线性表,特殊之处在于它只允许在表的前端 进行删除操作,而在表的后端 进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
#include<cstdio>
#include<queue>
using namespace std;
queue<int> q;
int n,opt,k;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&opt);
if(opt==1) scanf("%d",&k),q.push(k);
else if(opt==2) q.pop();
else printf("%d\n",q.front());
}
return 0;
}
复杂度(入队,出队,访问队首)
栈
【简介】
栈 又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
【代码实现】
#include<cstdio>
#include<stack>
using namespace std;
int n,opt,k;
stack<int> s;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&opt);
if(opt==1) scanf("%d",&k),s.push(k);
else if(opt==2) s.pop();
else printf("%d\n",s.top());
}
return 0;
}
复杂度(入栈,出栈,访问栈顶)
堆
【简介】
堆( )是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。
【代码实现】
二叉堆
#include<cstdio>
#include<queue>
#include<cctype>
using namespace std;
priority_queue<int,vector<int>,greater<int> > q;
inline int read(){
int x=0,f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
int main(){
int m=read();
for(int i=1;i<=m;i++){
int a=read();
if(a==1){
int b=read();
q.push(b);
}
else if(a==2) printf("%d\n"q.top());
else q.pop();
}
return 0;
}
*斐波那契堆
#ifndef _FIBONACCI_TREE_HPP_
#define _FIBONACCI_TREE_HPP_
#include <iomanip>
#include <iostream>
#include <cstdlib>
#include <cmath>
using namespace std;
template <class T>
class FibNode {
public:
T key; // 关键字(键值)
int degree; // 度数
FibNode<T> *left; // 左兄弟
FibNode<T> *right; // 右兄弟
FibNode<T> *child; // 第一个孩子节点
FibNode<T> *parent; // 父节点
bool marked; // 是否被删除第一个孩子
FibNode(T value):key(value), degree(0), marked(false),
left(NULL),right(NULL),child(NULL),parent(NULL) {
key = value;
degree = 0;
marked = false;
left = this;
right = this;
parent = NULL;
child = NULL;
}
};
template <class T>
class FibHeap {
private:
int keyNum; // 堆中节点的总数
int maxDegree; // 最大度
FibNode<T> *min; // 最小节点(某个最小堆的根节点)
FibNode<T> **cons; // 最大度的内存区域
public:
FibHeap();
~FibHeap();
// 新建key对应的节点,并将其插入到斐波那契堆中
void insert(T key);
// 移除斐波那契堆中的最小节点
void removeMin();
// 将other合并到当前堆中
void combine(FibHeap<T> *other);
// 获取斐波那契堆中最小键值,并保存到pkey中;成功返回true,否则返回false。
bool minimum(T *pkey);
// 将斐波那契堆中键值oldkey更新为newkey
void update(T oldkey, T newkey);
// 删除键值为key的节点
void remove(T key);
// 斐波那契堆中是否包含键值key
bool contains(T key);
// 打印斐波那契堆
void print();
// 销毁
void destroy();
private:
// 将node从双链表移除
void removeNode(FibNode<T> *node);
// 将node堆结点加入root结点之前(循环链表中)
void addNode(FibNode<T> *node, FibNode<T> *root);
// 将双向链表b链接到双向链表a的后面
void catList(FibNode<T> *a, FibNode<T> *b);
// 将节点node插入到斐波那契堆中
void insert(FibNode<T> *node);
// 将"堆的最小结点"从根链表中移除,
FibNode<T>* extractMin();
// 将node链接到root根结点
void link(FibNode<T>* node, FibNode<T>* root);
// 创建consolidate所需空间
void makeCons();
// 合并斐波那契堆的根链表中左右相同度数的树
void consolidate();
// 修改度数
void renewDegree(FibNode<T> *parent, int degree);
// 将node从父节点parent的子链接中剥离出来,并使node成为"堆的根链表"中的一员。
void cut(FibNode<T> *node, FibNode<T> *parent);
// 对节点node进行"级联剪切"
void cascadingCut(FibNode<T> *node) ;
// 将斐波那契堆中节点node的值减少为key
void decrease(FibNode<T> *node, T key);
// 将斐波那契堆中节点node的值增加为key
void increase(FibNode<T> *node, T key);
// 更新斐波那契堆的节点node的键值为key
void update(FibNode<T> *node, T key);
// 在最小堆root中查找键值为key的节点
FibNode<T>* search(FibNode<T> *root, T key);
// 在斐波那契堆中查找键值为key的节点
FibNode<T>* search(T key);
// 删除结点node
void remove(FibNode<T> *node);
// 销毁斐波那契堆
void destroyNode(FibNode<T> *node);
// 打印"斐波那契堆"
void print(FibNode<T> *node, FibNode<T> *prev, int direction);
};
/*
* 构造函数
*/
template <class T>
FibHeap<T>::FibHeap()
{
keyNum = 0;
maxDegree = 0;
min = NULL;
cons = NULL;
}
/*
* 析构函数
*/
template <class T>
FibHeap<T>::~FibHeap()
{
}
/*
* 将node从双链表移除
*/
template <class T>
void FibHeap<T>::removeNode(FibNode<T> *node)
{
node->left->right = node->right;
node->right->left = node->left;
}
/*
* 将node堆结点加入root结点之前(循环链表中)
* a …… root
* a …… node …… root
*/
template <class T>
void FibHeap<T>::addNode(FibNode<T> *node, FibNode<T> *root)
{
node->left = root->left;
root->left->right = node;
node->right = root;
root->left = node;
}
/*
* 将节点node插入到斐波那契堆中
*/
template <class T>
void FibHeap<T>::insert(FibNode<T> *node)
{
if (keyNum == 0)
min = node;
else
{
addNode(node, min);
if (node->key < min->key)
min = node;
}
keyNum++;
}
/*
* 新建键值为key的节点,并将其插入到斐波那契堆中
*/
template <class T>
void FibHeap<T>::insert(T key)
{
FibNode<T> *node;
node = new FibNode<T>(key);
if (node == NULL)
return ;
insert(node);
}
/*
* 将双向链表b链接到双向链表a的后面
*
* 注意: 此处a和b都是双向链表
*/
template <class T>
void FibHeap<T>::catList(FibNode<T> *a, FibNode<T> *b)
{
FibNode<T> *tmp;
tmp = a->right;
a->right = b->right;
b->right->left = a;
b->right = tmp;
tmp->left = b;
}
/*
* 将other合并到当前堆中
*/
template <class T>
void FibHeap<T>::combine(FibHeap<T> *other)
{
if (other==NULL)
return ;
if(other->maxDegree > this->maxDegree)
swap(*this, *other);
if((this->min) == NULL) // this无"最小节点"
{
this->min = other->min;
this->keyNum = other->keyNum;
free(other->cons);
delete other;
}
else if((other->min) == NULL) // this有"最小节点" && other无"最小节点"
{
free(other->cons);
delete other;
} // this有"最小节点" && other有"最小节点"
else
{
// 将"other中根链表"添加到"this"中
catList(this->min, other->min);
if (this->min->key > other->min->key)
this->min = other->min;
this->keyNum += other->keyNum;
free(other->cons);
delete other;
}
}
/*
* 将"堆的最小结点"从根链表中移除,
* 这意味着"将最小节点所属的树"从堆中移除!
*/
template <class T>
FibNode<T>* FibHeap<T>::extractMin()
{
FibNode<T> *p = min;
if (p == p->right)
min = NULL;
else
{
removeNode(p);
min = p->right;
}
p->left = p->right = p;
return p;
}
/*
* 将node链接到root根结点
*/
template <class T>
void FibHeap<T>::link(FibNode<T>* node, FibNode<T>* root)
{
// 将node从双链表中移除
removeNode(node);
// 将node设为root的孩子
if (root->child == NULL)
root->child = node;
else
addNode(node, root->child);
node->parent = root;
root->degree++;
node->marked = false;
}
/*
* 创建consolidate所需空间
*/
template <class T>
void FibHeap<T>::makeCons()
{
int old = maxDegree;
// 计算log2(keyNum),"+1"意味着向上取整!
// ex. log2(13) = 3,向上取整为3+1=4。
maxDegree = (log(keyNum)/log(2.0)) + 1;
if (old >= maxDegree)
return ;
// 因为度为maxDegree可能被合并,所以要maxDegree+1
cons = (FibNode<T> **)realloc(cons,
sizeof(FibHeap<T> *) * (maxDegree + 1));
}
/*
* 合并斐波那契堆的根链表中左右相同度数的树
*/
template <class T>
void FibHeap<T>::consolidate()
{
int i, d, D;
FibNode<T> *x, *y, *tmp;
makeCons();//开辟哈希所用空间
D = maxDegree + 1;
for (i = 0; i < D; i++)
cons[i] = NULL;
// 合并相同度的根节点,使每个度数的树唯一
while (min != NULL)
{
x = extractMin(); // 取出堆中的最小树(最小节点所在的树)
d = x->degree; // 获取最小树的度数
// cons[d] != NULL,意味着有两棵树(x和y)的"度数"相同。
while (cons[d] != NULL)
{
y = cons[d]; // y是"与x的度数相同的树"
if (x->key > y->key) // 保证x的键值比y小
swap(x, y);
link(y, x); // 将y链接到x中
cons[d] = NULL;
d++;
}
cons[d] = x;
}
min = NULL;
// 将cons中的结点重新加到根表中
for (i=0; i<D; i++)
{
if (cons[i] != NULL)
{
if (min == NULL)
min = cons[i];
else
{
addNode(cons[i], min);
if ((cons[i])->key < min->key)
min = cons[i];
}
}
}
}
/*
* 移除最小节点
*/
template <class T>
void FibHeap<T>::removeMin()
{
if (min==NULL)
return ;
FibNode<T> *child = NULL;
FibNode<T> *m = min;
// 将min每一个儿子(儿子和儿子的兄弟)都添加到"斐波那契堆的根链表"中
while (m->child != NULL)
{
child = m->child;
removeNode(child);
if (child->right == child)
m->child = NULL;
else
m->child = child->right;
addNode(child, min);
child->parent = NULL;
}
// 将m从根链表中移除
removeNode(m);
// 若m是堆中唯一节点,则设置堆的最小节点为NULL;
// 否则,设置堆的最小节点为一个非空节点(m->right),然后再进行调节。
if (m->right == m)
min = NULL;
else
{
min = m->right;
consolidate();
}
keyNum--;
delete m;
}
/*
* 获取斐波那契堆中最小键值,并保存到pkey中;成功返回true,否则返回false。
*/
template <class T>
bool FibHeap<T>::minimum(T *pkey)
{
if (min==NULL || pkey==NULL)
return false;
*pkey = min->key;
return true;
}
/*
* 修改度数
*/
template <class T>
void FibHeap<T>::renewDegree(FibNode<T> *parent, int degree)
{
parent->degree -= degree;
if (parent-> parent != NULL)
renewDegree(parent->parent, degree);
}
/*
* 将node从父节点parent的子链接中剥离出来,
* 并使node成为"堆的根链表"中的一员。
*/
template <class T>
void FibHeap<T>::cut(FibNode<T> *node, FibNode<T> *parent)
{
removeNode(node);
renewDegree(parent, node->degree);
// node没有兄弟
if (node == node->right)
parent->child = NULL;
else
parent->child = node->right;
node->parent = NULL;
node->left = node->right = node;
node->marked = false;
// 将"node所在树"添加到"根链表"中
addNode(node, min);
}
/*
* 对节点node进行"级联剪切"
*
* 级联剪切:如果减小后的结点破坏了最小堆性质,
* 则把它切下来(即从所在双向链表中删除,并将
* 其插入到由最小树根节点形成的双向链表中),
* 然后再从"被切节点的父节点"到所在树根节点递归执行级联剪枝
*/
template <class T>
void FibHeap<T>::cascadingCut(FibNode<T> *node)
{
FibNode<T> *parent = node->parent;
if (parent != NULL)
{
if (node->marked == false)
node->marked = true;
else
{
cut(node, parent);
cascadingCut(parent);
}
}
}
/*
* 将斐波那契堆中节点node的值减少为key
*/
template <class T>
void FibHeap<T>::decrease(FibNode<T> *node, T key)
{
FibNode<T> *parent;
if (min==NULL ||node==NULL)
return ;
if ( key>=node->key)
{
cout << "decrease failed: the new key(" << key <<") "
<< "is no smaller than current key(" << node->key <<")" << endl;
return ;
}
node->key = key;
parent = node->parent;
if (parent!=NULL && node->key < parent->key)
{
// 将node从父节点parent中剥离出来,并将node添加到根链表中
cut(node, parent);
cascadingCut(parent);
}
// 更新最小节点
if (node->key < min->key)
min = node;
}
/*
* 将斐波那契堆中节点node的值增加为key
*/
template <class T>
void FibHeap<T>::increase(FibNode<T> *node, T key)
{
FibNode<T> *child, *parent, *right;
if (min==NULL ||node==NULL)
return ;
if (key <= node->key)
{
cout << "increase failed: the new key(" << key <<") "
<< "is no greater than current key(" << node->key <<")" << endl;
return ;
}
// 将node每一个儿子(不包括孙子,重孙,...)都添加到"斐波那契堆的根链表"中
while (node->child != NULL)
{
child = node->child;
removeNode(child); // 将child从node的子链表中删除
if (child->right == child)
node->child = NULL;
else
node->child = child->right;
addNode(child, min); // 将child添加到根链表中
child->parent = NULL;
}
node->degree = 0;
node->key = key;
// 如果node不在根链表中,
// 则将node从父节点parent的子链接中剥离出来,
// 并使node成为"堆的根链表"中的一员,
// 然后进行"级联剪切"
// 否则,则判断是否需要更新堆的最小节点
parent = node->parent;
if(parent != NULL)
{
cut(node, parent);
cascadingCut(parent);
}
else if(min == node)
{
right = node->right;
while(right != node)
{
if(node->key > right->key)
min = right;
right = right->right;
}
}
}
/*
* 更新斐波那契堆的节点node的键值为key
*/
template <class T>
void FibHeap<T>::update(FibNode<T> *node, T key)
{
if(key < node->key)
decrease(node, key);
else if(key > node->key)
increase(node, key);
else
cout << "No need to update!!!" << endl;
}
template <class T>
void FibHeap<T>::update(T oldkey, T newkey)
{
FibNode<T> *node;
node = search(oldkey);
if (node!=NULL)
update(node, newkey);
}
/*
* 在最小堆root中查找键值为key的节点
*/
template <class T>
FibNode<T>* FibHeap<T>::search(FibNode<T> *root, T key)
{
FibNode<T> *t = root; // 临时节点
FibNode<T> *p = NULL; // 要查找的节点
if (root==NULL)
return root;
do
{
if (t->key == key)
{
p = t;
break;
}
else
{
if ((p = search(t->child, key)) != NULL)
break;
}
t = t->right;
} while (t != root);
return p;
}
/*
* 在斐波那契堆中查找键值为key的节点
*/
template <class T>
FibNode<T>* FibHeap<T>::search(T key)
{
if (min==NULL)
return NULL;
return search(min, key);
}
/*
* 在斐波那契堆中是否存在键值为key的节点。
* 存在返回true,否则返回false。
*/
template <class T>
bool FibHeap<T>::contains(T key)
{
return search(key)!=NULL ? true: false;
}
/*
* 删除结点node
*/
template <class T>
void FibHeap<T>::remove(FibNode<T> *node)
{
T m = min->key-1;
decrease(node, m-1);
removeMin();
}
template <class T>
void FibHeap<T>::remove(T key)
{
FibNode<T> *node;
if (min==NULL)
return ;
node = search(key);
if (node==NULL)
return ;
remove(node);
}
/*
* 销毁斐波那契堆
*/
template <class T>
void FibHeap<T>::destroyNode(FibNode<T> *node)
{
FibNode<T> *start = node;
if(node == NULL)
return;
do {
destroyNode(node->child);
// 销毁node,并将node指向下一个
node = node->right;
delete node->left;
} while(node != start);
}
template <class T>
void FibHeap<T>::destroy()
{
destroyNode(min);
free(cons);
}
/*
* 打印"斐波那契堆"
*
* 参数说明:
* node -- 当前节点
* prev -- 当前节点的前一个节点(父节点or兄弟节点)
* direction -- 1,表示当前节点是一个左孩子;
* 2,表示当前节点是一个兄弟节点。
*/
template <class T>
void FibHeap<T>::print(FibNode<T> *node, FibNode<T> *prev, int direction)
{
FibNode<T> *start=node;
if (node==NULL)
return ;
do
{
if (direction == 1)
cout << setw(8) << node->key << "(" << node->degree << ") is "<< setw(2) << prev->key << "'s child" << endl;
else
cout << setw(8) << node->key << "(" << node->degree << ") is "<< setw(2) << prev->key << "'s next" << endl;
if (node->child != NULL)
print(node->child, node, 1);
// 兄弟节点
prev = node;
node = node->right;
direction = 2;
} while(node != start);
}
template <class T>
void FibHeap<T>::print()
{
int i=0;
FibNode<T> *p;
if (min==NULL)
return ;
cout << "== 斐波那契堆的详细信息: ==" << endl;
p = min;
do {
i++;
cout << setw(2) << i << ". " << setw(4) << p->key << "(" << p->degree << ") is root" << endl;
print(p->child, p, 1);
p = p->right;
} while (p != min);
cout << endl;
}
#endif
*左偏堆
//bzoj 1455
#include <iostream>
#include <cstdio>
using namespace std;
const int N=1000001;
int n,m; int a[N]; bool die[N];
int l[N],r[N],fa[N],dep[N];
inline int read(){
int x=0,f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int merge(int x,int y){
if(!x)return y;
if(!y)return x;
if(a[x]>a[y]) swap(x,y);
r[x]=merge(r[x],y);
if(dep[r[x]]>dep[l[x]])swap(l[x],r[x]);
dep[x]=dep[r[x]]+1;
return x;
}
void work(){
char ch[10]; int x,y;
for(int i=1;i<=m;i++){
scanf("%s",ch);
if(ch[0]=='M') {
x=read(),y=read();
if (die[x]||die[y]) continue;
int f1=find(x),f2=find(y);
if(f1!=f2){
int t=merge(f1,f2);
fa[f2]=fa[f1]=t;
}
}
else {
x=read();
if(die[x])printf("0\n");
else{
int f1=find(x);die[f1]=1;
printf("%d\n",a[f1]);
fa[f1]=merge(l[f1],r[f1]);
fa[fa[f1]]=fa[f1];
}
}
}
return;
}
int main(){
n=read();
for(int i=1;i<=n;i++)a[i]=read(),fa[i]=i;
m=read();
work();
return 0;
}
*配对堆
//Luogu P3371
#include<bits/stdc++.h>
using namespace std;
const int M=2e6+5;
int n,root,id,tot,top,val[M],head[M],nxt[M],to[M],dad[M];
queue<int>dui;
void add(int x,int y){
nxt[++id]=head[x],head[x]=id,to[id]=y,dad[y]=x;
}
int _new(int x){
val[++tot]=x;
return tot;
}
int merge(int x,int y){
if(!x||!y)return x+y;
if(val[x]<val[y]){
add(x,y);
return x;
}
else{
add(y,x);
return y;
}
}
void pop(){
int t;
for(int i=head[root]; i; i=nxt[i])if(dad[to[i]]==root)dui.push(to[i]),dad[to[i]]=0;
while(dui.size()>1){
t=dui.front();
dui.pop();
dui.push(merge(dui.front(),t));
dui.pop();
}
if(dui.size())root=dui.front(),dui.pop();
else root=0;
}
int main(){
scanf("%d",&n);
int op,x;
for(int i=1; i<=n; ++i){
scanf("%d",&op);
if(op==1)scanf("%d",&x),root=merge(_new(x),root);
else if(op==2)printf("%d\n",val[root]);
else pop();
}
}
并查集
【简介】
并查集,在一些有 个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中 。
并查集是一种树型的数据结构,用于处理一些不相交集合 的合并及查询问题。常常在使用中以森林来表示。
【代码实现】
朴素并查集(带路径压缩)
//https://www.luogu.org/problemnew/show/P3367
#include<iostream>
#include<cstdio>
#include<ctype.h>
using namespace std;
inline int read(){
int x=0,f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
int fa[200007];
int find(int x){
if(fa[x]!=x) return fa[x]=find(fa[x]);
else return x;
}
int main(){
int n=read(),m=read();
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++){
int o=read(),a=read(),b=read();
if(o==1){
a=find(a),b=find(b);
if(a!=b) fa[b]=a;
}
else printf(find(a)==find(b)?"Y\n":"N\n");
}
return 0;
}
void MergeSet(int x,int y){
srand(time(NULL));
x=find(x),y=find(y);
if(x!=y){
if(rand()&1) fa[x]=y;
else fa[y]=x;
}
return;
}
void MergeSet(int x,int y){
x=find(x),y=find(y);
if(x!=y){
if(rank[x]<=rank[y]) fa[x]=y,rank[y]=max(rank[y],rank[x]+1);
else fa[y]=x,rank[x]=max(rank[x],rank[y]+1);
}
}
复杂度:路径压缩+按秩合并: ,其他:
*可持久化并查集
//bzoj 3674
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=200100;
const int maxl=20;
struct data{
int lson,rson,id,val,_Size;
} tree[maxn+2*maxn*maxl];
int Root[maxn];
int cur;
int w[maxn];
int n,m,lt,nt,lans;
void Build(int x){
tree[x].id=x;
tree[x].val=x;
tree[x]._Size=1;
int left=x<<1;
if (left<=n){
tree[x].lson=left;
Build(left);
}
int right=left|1;
if (right<=n){
tree[x].rson=right;
Build(right);
}
}
data Query(int root,int L,int x){
if (x==L) return tree[root];
if (x&(1<<(w[x]-w[L]-1))) return Query(tree[root].rson,L*2+1,x);
else return Query(tree[root].lson,L*2,x);
}
data Find_fa(int x){
data y=Query(Root[lt],1,x);
while (y.id!=y.val) y=Query(Root[lt],1,y.val);
return y;
}
void Update(int root,int L,int x,int nid,int v){
if (L==x){
if (nid==0) tree[root].val=v;
else tree[root]._Size=v;
return;
}
if (x&(1<<(w[x]-w[L]-1))){
data temp=tree[ tree[root].rson ];
tree[root].rson=++cur;
tree[cur]=temp;
Update(cur,L*2+1,x,nid,v);
}
else{
data temp=tree[ tree[root].lson ];
tree[root].lson=++cur;
tree[cur]=temp;
Update(cur,L*2,x,nid,v);
}
}
void Add(int x,int y){
data fx=Find_fa(x);
data fy=Find_fa(y);
if (fx.val==fy.val){
Root[nt]=Root[lt];
return;
}
if (fx._Size>fy._Size) swap(fx,fy);
Root[nt]=++cur;
tree[cur]=tree[ Root[lt] ];
Update(cur,1,fx.id,0,fy.id);
tree[++cur]=tree[ Root[nt] ];
Root[nt]=cur;
Update(cur,1,fy.id,1,fx._Size+fy._Size);
}
int main(){
freopen("c.in","r",stdin);
freopen("c.out","w",stdout);
scanf("%d%d",&n,&m);
Build(1);
Root[1]=1;
cur=n;
lt=1,nt=1;
lans=0;
w[1]=1;
for (int i=2; i<maxn; i++) w[i]=w[i/2]+1;
for (int i=1; i<=m; i++){
int op;
scanf("%d",&op);
if (op==1){
int a,b;
scanf("%d%d",&a,&b);
a^=lans,b^=lans;
nt++;
Add(a,b);
lt=nt;
}
if (op==2){
int k;
scanf("%d",&k);
k^=lans;
lt=k+1;
nt++;
Root[nt]=Root[lt];
lt=nt;
}
if (op==3){
int a,b;
scanf("%d%d",&a,&b);
a^=lans,b^=lans;
data fa=Find_fa(a);
data fb=Find_fa(b);
bool f=(fa.val==fb.val);
printf("%d\n",f);
lans=f;
nt++;
Root[nt]=Root[lt];
lt=nt;
}
}
return 0;
}
单调队列
【简介】
单调队列,即单调递减或单调递增的队列。
【代码实现】
//https://www.luogu.org/problemnew/show/P1886
#include<cstdio>
int q[1000100],p[1000100],a[1000100];
int head,tail,n,k;
void Max(){
head=1;tail=0;
for(int i=1;i<=n;i++){
while(tail>=head && q[tail]<=a[i]) tail--;
q[++tail]=a[i];
p[tail]=i;
while(i-k+1>p[head]) head++;
if(i>=k) printf("%d ",q[head]);
}
}
void Min(){
head=1;tail=0;
for(int i=1;i<=n;i++){
while(head<=tail && q[tail]>=a[i]) tail--;
q[++tail]=a[i];
p[tail]=i;
while(i-k+1>p[head]) head++;
if(i>=k) printf("%d ",q[head]);
}
}
int main(){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
Min();
printf("\n");
Max();
return 0;
}
复杂度
单调栈
【简介】
单调递增或单调减的栈,跟单调队列
差不多,但是只用到它的一端,利用它可以用来解决一些
和
的题目。
【代码实现】
//poj 3250
#include<cstdio>
#include<iostream>
#include<stack>
using namespace std;
int main(){
int i,n,top,a[80010]; //top指向栈顶元素
long long ans; //存储最终结果
stack<int> st; //st为栈,每次入栈的是每头牛的位置
while(~scanf("%d",&n)){
while(!st.empty()) st.pop(); //清空栈
for(i=0;i<n;i++) scanf("%d",&a[i]);
a[n]=2147483647; //将最后一个元素设为最大值
ans=0;
for(i=0;i<=n;i++){
if(st.empty()||a[i]<a[st.top()])st.push(i);
//如果栈为空或入栈元素小于栈顶元素,则入栈
else{
while(!st.empty()&&a[i]>=a[st.top()]){//如果栈不为空并且栈顶元素不大于入栈元素,则将栈顶元素出栈
top=st.top(); //获取栈顶元素
st.pop(); //栈顶元素出栈
//这时候也就找到了第一个比栈顶元素大的元素
//计算这之间牛的个数,为下标之差-1
ans+=(i-top-1);
}
st.push(i); //当所有破坏栈的单调性的元素都出栈后,将当前元素入栈
}
}
cout<<ans;
}
return 0;
}
复杂度
线段树
【简介】
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为
。而未优化的空间复杂度为
,实际应用时一般还要开
的数组以免越界,因此有时需要离散化让空间压缩。
【代码实现】
朴素线段树
线段树
//https://www.luogu.org/problemnew/show/P3372
#include<iostream>
#include<cstdio>
#include<ctype.h>
typedef long long ll;
inline ll read(){
ll x=0;int f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
ll ans,L,R,w;
struct Tree{
ll l,r,w,flag;
}tree[262144+7];
void update(int o){
tree[o].w=tree[o<<1].w+tree[o<<1|1].w;
}
void Down(int o){
tree[o<<1].flag+=tree[o].flag;
tree[o<<1|1].flag+=tree[o].flag;
tree[o<<1].w+=(tree[o<<1].r-tree[o<<1].l+1)*tree[o].flag;
tree[o<<1|1].w+=(tree[o<<1|1].r-tree[o<<1|1].l+1)*tree[o].flag;
tree[o].flag=0;
}
void build(int o,int l,int r){
tree[o].l=l,tree[o].r=r;
if(l==r){
tree[o].w=read();
return ;
}
int mid=(l+r)>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
update(o);
}
void add_point(int o){
if(tree[o].l==tree[o].r){
ans+=tree[o].w;
return ;
}
if(tree[o].flag) Down(o);
int mid=(tree[o].l+tree[o].r)>>1;
if(L<=mid) add_point(o<<1);
else add_point(o<<1|1);
update(o);
}
void ask_interval(int o){
if(tree[o].l>=L && tree[o].r<=R){
ans+=tree[o].w;
return ;
}
if(tree[o].flag) Down(o);
int mid=(tree[o].l+tree[o].r)>>1;
if(L<=mid)ask_interval(o<<1);
if(R>mid) ask_interval(o<<1|1);
}
void add_interval(int o){
if(tree[o].l>=L && tree[o].r<=R){
tree[o].w+=(tree[o].r-tree[o].l+1)*w;
tree[o].flag+=w;
return ;
}
if(tree[o].flag) Down(o);
int mid=(tree[o].l+tree[o].r)>>1;
if(L<=mid) add_interval(o<<1);
if(R>mid) add_interval(o<<1|1);
update(o);
}
int main(){
int n=read(),m=read();
build(1,1,n);
for(int i=1;i<=m;++i){
int ooo=read();L=read(),R=read();
if(ooo==1){
w=read();
add_interval(1);
}
else {
ans=0;ask_interval(1);
printf("%lld\n",ans);
}
}
return 0;
}
线段树
//https://www.luogu.org/problemnew/show/P3373
#include<iostream>
#include<cstdio>
#include<ctype.h>
using namespace std;
inline int read(){
int x=0,f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
int L,R,W,mod;
long long ans;
struct Tree{
int l,r;
long long w,add,mul;
}tree[262144+7];
inline void update(int o){
tree[o].w=(tree[o<<1].w+tree[o<<1|1].w)%mod;
}
inline void Down(int o){
if(tree[o].mul!=1){
tree[o<<1].w=tree[o<<1].w*tree[o].mul%mod;
tree[o<<1|1].w=tree[o<<1|1].w*tree[o].mul%mod;
tree[o<<1].add=tree[o<<1].add*tree[o].mul%mod;
tree[o<<1|1].add=tree[o<<1|1].add*tree[o].mul%mod;
tree[o<<1].mul=tree[o<<1].mul*tree[o].mul%mod;
tree[o<<1|1].mul=tree[o<<1|1].mul*tree[o].mul%mod;
tree[o].mul=1;
}
if(tree[o].add){
tree[o<<1].w=(tree[o<<1].w+(tree[o<<1].r-tree[o<<1].l+1)*tree[o].add%mod)%mod;
tree[o<<1|1].w=(tree[o<<1|1].w+(tree[o<<1|1].r-tree[o<<1|1].l+1)*tree[o].add%mod)%mod;
tree[o<<1].add=(tree[o<<1].add+tree[o].add)%mod;
tree[o<<1|1].add=(tree[o<<1|1].add+tree[o].add)%mod;
tree[o].add=0;
}
}
void ask_interval(int o){
if(tree[o].l>=L && tree[o].r<=R){
ans=(ans+tree[o].w)%mod;
return ;
}
Down(o);
int mid=tree[o].l+tree[o].r>>1;
if(L<=mid)ask_interval(o<<1);
if(R>mid)ask_interval(o<<1|1);
}
void add_interval(int o){
if(tree[o].l>=L && tree[o].r<=R){
tree[o].w=(tree[o].w+(tree[o].r-tree[o].l+1)*W%mod)%mod;
tree[o].add+=W;
return ;
}
Down(o);
int mid=tree[o].l+tree[o].r>>1;
if(L<=mid)add_interval(o<<1);
if(R>mid)add_interval(o<<1|1);
update(o);
}
void mul_interval(int o){
if(tree[o].l>=L && tree[o].r<=R){
tree[o].w=tree[o].w*W%mod;
tree[o].add=tree[o].add*W%mod;
tree[o].mul=tree[o].mul*W%mod;
return ;
}
Down(o);
int mid=tree[o].l+tree[o].r>>1;
if(L<=mid)mul_interval(o<<1);
if(R>mid)mul_interval(o<<1|1);
update(o);
}
void build(int o,int l,int r){
tree[o].mul=1;
tree[o].l=l,tree[o].r=r;
if(l==r){
tree[o].w=read();
return ;
}
int mid=l+r>>1;
build(o<<1,l,mid),build(o<<1|1,mid+1,r);
update(o);
}
int main(){
int n=read(),m=read();mod=read();
build(1,1,n);
for(int i=1;i<=m;++i){
int o=read();L=read(),R=read();
if(o==3){
ans=0;ask_interval(1);
printf("%lld\n",ans);
}
else{
W=read();
o&1?mul_interval(1):add_interval(1);
}
}
return 0;
}
复杂度
*可持久化线段树(主席树)
//https://www.luogu.org/problemnew/show/P3834
#include<iostream>
#include<cstdio>
#include<ctype.h>
#include<algorithm>
using namespace std;
inline int read(){
int x=0,f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
struct Tree{
int lc,rc,ans;
}tree[3600007];
int rt[200007],a[200007],s[200007],tot;
void insert(int &root,int last,int l,int r,int k){
root=++tot;
tree[root].lc=tree[last].lc;
tree[root].rc=tree[last].rc;
tree[root].ans=tree[last].ans+1;
if(l==r) return ;
int mid=l+r>>1;
if(k<=mid)insert(tree[root].lc,tree[last].lc,l,mid,k);
else insert(tree[root].rc,tree[last].rc,mid+1,r,k);
}
int quary(int L,int R,int l,int r,int k){
if(l==r) return l;
int mid=l+r>>1,o=tree[tree[R].lc].ans-tree[tree[L].lc].ans;
if(k<=o) return quary(tree[L].lc,tree[R].lc,l,mid,k);
else return quary(tree[L].rc,tree[R].rc,mid+1,r,k-o);
}
int main(){
int n=read(),m=read();
for(int i=1;i<=n;++i)s[i]=a[i]=read();
sort(s+1,s+1+n);
int len=unique(s+1,s+n+1)-s-1;
for(int i=1;i<=n;++i){
int o=lower_bound(s+1,s+len+1,a[i])-s;
insert(rt[i],rt[i-1],1,len,o);
}
while(m--){
int l=read(),r=read(),k=read();
printf("%d\n",s[quary(rt[l-1],rt[r],1,len,k)]);
}
return 0;
}
*权值线段树
//https://www.luogu.org/problemnew/show/P1801
#include<bits/stdc++.h>
#define N 200005
using namespace std;
int m,n,k;
int a[N],b[N],u[N];
struct MM{
int l,r,s;
}tree[N<<2];
inline void build(int root,int L,int R){
tree[root].l=L;
tree[root].r=R;
if(L==R) return;
int mid=(L+R)>>1;
build(root<<1,L,mid);
build(root<<1|1,mid+1,R);
}
inline void update(int root,int t){
if(tree[root].l==tree[root].r){
tree[root].s++;//个数加一
return;
}
int mid=(tree[root].l+tree[root].r)>>1;
if(t<=mid) update(root<<1,t);
else update(root<<1|1,t);
tree[root].s=tree[root<<1].s+tree[root<<1|1].s;
}
inline int query(int root,int t){
if(tree[root].l==tree[root].r)
return tree[root].l;
if(t<=tree[root<<1].s) return query(root<<1,t);
else return query(root<<1|1,t-tree[root<<1].s);
}
int main(){
cin>>m>>n;
for(int i=1;i<=m;i++){
cin>>a[i];
b[i]=a[i];
}
for(int i=1;i<=n;i++)cin>>u[i];
sort(b+1,b+m+1);
int s=unique(b+1,b+m+1)-(b+1);//离散化(如果值域很大的话),s是数组b中不重复的数的个数
build(1,1,s);//依s建树
int h=0;
while(n!=h){
h++;
for(int i=u[h-1]+1;i<=u[h];i++){
int v=lower_bound(b+1,b+s+1,a[i])-b;//v是a[i]在数组b中所处的位置(注意之前数组b排了序)
update(1,v);
}
cout<<b[query(1,++k)]<<endl;
}
return 0;
}
*动态开点线段树
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define clr(a,b) memset(a,b,sizeof a)
#define rep(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int maxn = 2e6+5e5+11;
const int oo = 0x3f3f3f3f;
int T[70];
struct ST{
int L[maxn<<2],R[maxn<<2],minx[maxn<<2];
int tot;
void init(){
clr(T,0);
tot=0;
L[0]=R[0]=0;
minx[0]=oo;
}
void pu(int o){
minx[o]=min(minx[L[o]],minx[R[o]]);
}
void update(int &o,int l,int r,int y,int x){
if(!o){
o=++tot;
L[o]=R[o]=0;
minx[o]=x;
}
if(l==r){
minx[o]=min(minx[o],x);
return ;
}
int m=l+r>>1;
if(y<=m) update(L[o],l,m,y,x);
else update(R[o],m+1,r,y,x);
pu(o);
}
int query(int &o,int l,int r,int LL,int RR){
if(!o){
return oo; //很重要!!!
}
if(LL<=l&&r<=RR){
return minx[o];
}
int m=l+r>>1;
int ans=oo;
if(LL<=m) ans=min(ans,query(L[o],l,m,LL,RR));
if(RR>m) ans=min(ans,query(R[o],m+1,r,LL,RR));
return ans;
}
}st;
const int n = 1e6;
int main(){
int op,x,y,yy1,yy2,c;
st.init();
while(scanf("%d",&op)!=EOF){
if(op==3)break;
else if(op==0) st.init();
else if(op==1){
scanf("%d%d%d",&x,&y,&c);
st.update(T[c],1,n,y,x);
}
else if(op==2){
scanf("%d%d%d",&x,&yy1,&yy2);
int ans=0,tmp;
rep(i,0,50){
tmp=st.query(T[i],1,n,yy1,yy2);
if(tmp<=x) ans++;
}
printf("%d\n",ans);
}
}
return 0;
}
树状数组
【简介】
树状数组 , 是一个查询和修改复杂度都为 的数据结构。主要用于查询任意两位之间的所有元素之和,但是每次只能修改一个元素的值;经过简单修改可以在 的复杂度下进行范围修改,但是这时只能查询其中一个元素的值(如果加入多个辅助数组则可以实现区间修改与区间查询)。
【代码实现】
#include<iostream>
#include<ctype.h>
#include<cstdio>
#include<algorithm>
#define lowbit(x) x&(-x)
using namespace std;
long long n,q,C[1000007];
inline int read(){
int x=0,f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
inline void add(int x,int y){
while(x<=n)C[x]+=y,x+=lowbit(x);
}
inline long long getsum(int x){
long long ans=0;
while(x>0) ans+=C[x],x-=lowbit(x);
return ans;
}
int main(){
n=read(),q=read();
// cout<<n<<q;
for(int i=1;i<=n;++i){
int x=read();add(i,x);
}
while(q--){
int o=read(),l=read(),r=read();
if(o&1)add(l,r);
else printf("%lld\n",getsum(r)-getsum(l-1));
}
return 0;
}
复杂度
ST表
【简介】
表类似树状数组,线段树这两种数据结构,是一种用于解决 ,即区间最值查询)问题的离线数据结构。
【代码实现】
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int f[100001][40];
int n,m,l,r;
int main(){
scanf("%d%d",&n,&m);
int p=log2(n);
for(int i=1;i<=n;i++) scanf("%d",&f[i][0]);
for(int j=1;j<=p;j++)
for (int i=1;i<=n-(1<<j)+1;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
for(int i=1;i<=m;i++){
scanf("%d%d",&l,&r);
p=log2(r-l+1);
printf("%d\n",max(f[l][p],f[r-(1<<p)+1][p]));
}
return 0;
}
复杂度
*块状链表
【简介】
其基本定应用为:把一个长度为 的串,分成约块,相邻两块的大小不小于根号 ,每一块的大小不超过 。这样就可以在的时间内解决一个插入、询问、拆分、合并等等的操作。
【代码实现】
//bzoj 3337
#include<cstring>
#include<iostream>
#include<cctype>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cmath>
#define writ(x,c) write(x),putchar(c);
typedef long long ll;
const int N=10000,MAX=1<<29;
using namespace std;
inline int read(){
char c;int x=0;bool f=0;
for(;!isdigit(c);c=getchar()) if(c=='-') f=1;
for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);
return (f ? -x : x);
}
template <class _T>
void write(_T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
int n,m,size;
struct node{
int d[1003],s[1003],rev,add,same,size,next;
ll sum;
}a[N];
queue<int> q;
void Init(){}
int new_node(){
int temp=q.front();q.pop();
return temp;
}
void clear(int x){a[x].rev=a[x].add=a[x].same=a[x].size=a[x].rev=0;}
void erase(int x){q.push(x);clear(x);}
void find(int &pos,int &now){
for(now=0;a[now].next!=-1&&pos>a[now].size;now=a[now].next)
pos-=a[now].size;
}
void down(int now){
int tot=a[now].size;
if(a[now].rev){
a[now].rev=false;
int tt=(tot>>1);
for(int u=1;u<=tt;u++) swap(a[now].d[u],a[now].d[tot-u+1]);
}
if(a[now].same!=0){
for(int u=1;u<=tot;u++)
a[now].d[u]=a[now].same;
a[now].sum=a[now].same*tot,a[now].same=0;
}
if(a[now].add!=0){
for(int u=1;u<=tot;u++)
a[now].d[u]+=a[now].add;
a[now].sum=a[now].sum+a[now].add*tot,a[now].add=0;
}
}
void update(int x){
a[x].sum=0;
for(int u=1;u<=a[x].size;u++)
a[x].sum+=a[x].d[u],a[x].s[u]=a[x].d[u];
sort(a[x].s+1,a[x].s+a[x].size+1);
}
void spilt(int now,int pos){
down(now);
int t=new_node();
for(int u=pos;u<=a[now].size;u++)
a[t].d[++a[t].size]=a[now].d[u];
a[t].next=a[now].next,a[now].next=t,a[now].size=max(pos-1,0);
update(t);update(now);
}
void Merge(int now){
int k=a[now].next;
down(now);down(k);
for(int u=1;u<=a[k].size;u++)
a[now].d[++a[now].size]=a[k].d[u];
a[now].next=a[k].next;erase(k);
update(now);
}
void maintain(int now){
for(;now!=-1;now=a[now].next)
if(a[now].next!=-1&&a[now].size+a[a[now].next].size<=size)
Merge(now);
}
void ins(int pos,int x){
int now;pos++;
find(pos,now);spilt(now,pos);
a[now].d[++a[now].size]=x,a[now].sum+=x;
int lalal;
for(lalal=1;lalal<a[now].size;lalal++)
if(a[now].s[lalal]>x) break;
for(int u=a[now].size;u>lalal;u--)
a[now].s[u]=a[now].s[u-1];
a[now].s[lalal]=x;
maintain(now);
}
void del(int pos){
int now;
find(pos,now);down(now);
for(int u=pos+1;u<=a[now].size;u++)
a[now].d[u-1]=a[now].d[u];
a[now].size--;
update(now);maintain(now);
}
void solve(int l,int r,int &lp,int &rp){
int pos=l;
find(pos,lp);spilt(lp,pos);
pos=r+1;
find(pos,rp);spilt(rp,pos);
pos=r;
find(pos,rp);
}
int st[N];
void rverse(int l,int r){
int lp,rp;
solve(l,r,lp,rp);
int now=lp,top=0;
for(int u=a[lp].next;u!=a[rp].next;u=a[u].next)
st[++top]=u,a[u].rev^=1;
a[st[1]].next=a[rp].next;
for(int u=top;u>1;u--)
a[st[u]].next=st[u-1];
a[lp].next=rp;
maintain(lp);
}
void slip(int l,int r,int k){
int lp,mp,rp,np;
solve(l,r-k,lp,mp),solve(r-k+1,r,mp,rp);
np=a[lp].next,a[lp].next=a[mp].next,a[mp].next=a[rp].next,a[rp].next=np;
maintain(lp);
}
void add(int l,int r,int val){
int lp,rp;
solve(l,r,lp,rp);
for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
a[now].add+=val,a[now].sum=a[now].sum+a[now].size*val;
maintain(lp);
}
void same(int l,int r,int val){
int lp,rp;
solve(l,r,lp,rp);
for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
a[now].add=0,a[now].same=val,a[now].sum=a[now].size*val;
maintain(lp);
}
ll getsum(int l,int r){
int lp,rp;
solve(l,r,lp,rp);
ll ans=0;
for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
ans=ans+a[now].sum;
maintain(lp);
return ans;
}
int getcha(int l,int r){
int lp,rp;
solve(l,r,lp,rp);
int maxx=-MAX,minn=MAX;
for(int now=a[lp].next;now!=a[rp].next;now=a[now].next)
if(a[now].size!=0)
if(a[now].same!=0)
minn=min(minn,a[now].same+a[now].add),
maxx=max(maxx,a[now].same+a[now].add);
else
minn=min(minn,a[now].s[1]+a[now].add),
maxx=max(maxx,a[now].s[a[now].size]+a[now].add);
maintain(lp);
return maxx-minn;
}
int near(int l,int r,int val){
int lp,rp;
solve(l,r,lp,rp);
int ans=MAX;
for(int now=a[lp].next;now!=a[rp].next;now=a[now].next){
if(a[now].same)
ans=min(ans,abs(val-a[now].same-a[now].add));
else {
int id=lower_bound(a[now].s+1,a[now].s+a[now].size+1,val-a[now].add)-a[now].s;
if(id!=a[now].size+1)
ans=min(ans,a[now].s[id]+a[now].add-val);
if(id!=1)
id--,ans=min(ans,val-a[now].s[id]-a[now].add);
}
}
maintain(lp);
return ans;
}
int rank(int l,int r,int k){
int lp,rp;
solve(l,r,lp,rp);
int ll=0,rr=MAX;
while(ll<rr){
int mid=(ll+rr)/2+1,sum=1;
for(int now=a[lp].next;now!=a[rp].next;now=a[now].next){
if(a[now].same!=0){
if(a[now].same+a[now].add<mid)
sum=sum+a[now].size;
}
else{
int id=upper_bound(a[now].s+1,a[now].s+a[now].size+1,mid-a[now].add-1)-a[now].s;
sum=sum+max(0,id-1);
}
}
if(k>=sum) ll=mid;
else rr=mid-1;
}
maintain(lp);
return ll;
}
int sec(int l,int r,int val){
int lp,rp;
solve(l,r,lp,rp);
int ans=0;
for(int now=a[lp].next;now!=a[rp].next;now=a[now].next){
if(a[now].same!=0){
if(a[now].same+a[now].add<val)
ans=ans+a[now].size;
}
else{
int it=upper_bound(a[now].s+1,a[now].s+a[now].size+1,val-a[now].add-1)-a[now].s;
ans=ans+it-1;
}
}
maintain(lp);
return ans;
}
int main(){
n=read(),size=sqrt(n);
for(int i=1;i<N;i++) q.push(i);a[0].next=-1;a[0].size=0;
for(int u=1;u<=n;u++){
int x=read();ins(u-1,x);
}
m=read();
while(m--){
register int op,x,y,z;op=read();
switch(op){
case 1:x=read();y=read();ins(x,y);break;
case 2:x=read();del(x);break;
case 3:x=read();y=read();rverse(x,y);break;
case 4:x=read();y=read();z=read();slip(x,y,z);break;
case 5:x=read();y=read();z=read();add(x,y,z);break;
case 6:x=read();y=read();z=read();same(x,y,z);break;
case 7:x=read();y=read();writ(getsum(x,y),'\n');break;
case 8:x=read();y=read();writ(getcha(x,y),'\n');break;
case 9:x=read();y=read();z=read();writ(near(x,y,z),'\n');break;
case 10:x=read();y=read();z=read();writ(rank(x,y,z),'\n');break;
case 11:x=read();y=read();z=read();writ(sec(x,y,z),'\n');break;
}
}
return 0;
}
/*这代码简直恶心死了*/
复杂度
*Splay(伸展树)
【简介】
伸展树 ,也叫分裂树,是一种二叉排序树,它能在 内完成插入、查找和删除操作。它由 和 创造,后勃刚对其进行了改进。它的优势在于不需要记录用于平衡树的冗余信息。在伸展树上的一般操作都基于伸展操作。
【代码实现】
//https://www.luogu.org/problemnew/show/P3391
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,m;
int fa[N],ch[N][2],size[N],rev[N],rt;
inline void pushup(int x){
size[x]=size[ch[x][0]]+size[ch[x][1]]+1;
}
void pushdown(int x){
if(rev[x]){
swap(ch[x][0],ch[x][1]);
rev[ch[x][0]]^=1;rev[ch[x][1]]^=1;rev[x]=0;
}
}
void rotate(int x,int &k){
int y=fa[x],z=fa[y],kind;
if(ch[y][0]==x)kind=1;else kind=0;
if(y==k)k=x;
else{if(ch[z][0]==y)ch[z][0]=x;else ch[z][1]=x;}
ch[y][kind^1]=ch[x][kind];fa[ch[y][kind^1]]=y;
ch[x][kind]=y;fa[y]=x;fa[x]=z;
pushup(x);pushup(y);
}
void splay(int x,int &k){
while(x!=k){
int y=fa[x],z=fa[y];
if(y!=k){
if((ch[y][0]==x)^(ch[z][0]==y))rotate(x,k);
else rotate(y,k);
}
rotate(x,k);
}
}
void build(int l,int r,int f){
if(l>r)return;
int mid=(l+r)/2;
if(mid<f)ch[f][0]=mid;else ch[f][1]=mid;
fa[mid]=f;size[mid]=1;
if(l==r)return;
build(l,mid-1,mid);build(mid+1,r,mid);
pushup(mid);
}
int find(int x,int k){
pushdown(x);int s=size[ch[x][0]];
if(k==s+1)return x;
if(k<=s)return find(ch[x][0],k);
else return find(ch[x][1],k-s-1);
}
void rever(int l,int r){
int x=find(rt,l),y=find(rt,r+2);
splay(x,rt);splay(y,ch[x][1]);int z=ch[y][0];
rev[z]^=1;
}
int main(){
scanf("%d%d",&n,&m);
rt=(n+3)/2;build(1,n+2,rt);
for(int i=1;i<=m;i++){
int L,R;scanf("%d%d",&L,&R);
rever(L,R);
}
for(int i=2;i<=n+1;i++)printf("%d ",find(rt,i)-1);
return 0;
}
复杂度
*Treap
【简介】
树堆,在数据结构中也称
,是指有一个随机附加域满足堆的性质的二叉搜索树,其结构相当于以随机数据插入的二叉搜索树。其基本操作的期望时间复杂度为
。
相对于其他的平衡二叉搜索树,
的特点是实现简单 (这道题一点都不简单) ,且能基本实现随机平衡的结构。
【代码实现】
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#include<climits>
typedef long long LL;
using namespace std;
int RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
//第一次打treap,不压行写注释XD
const int maxn = 1000019,INF = 1e9;
//平衡树,利用BST性质查询和修改,利用随机和堆优先级来保持平衡,把树的深度控制在log N,保证了操作效率
//基本平衡树有以下几个比较重要的函数:新建,插入,删除,旋转
//节点的基本属性有val(值),dat(随机出来的优先级)
//通过增加属性,结合BST的性质可以达到一些效果,如size(子树大小,查询排名),cnt(每个节点包含的副本数)等
int na;
int ch[maxn][2];//[i][0]代表i左儿子,[i][1]代表i右儿子
int val[maxn],dat[maxn];
int size[maxn],cnt[maxn];
int tot,root;
int New(int v){//新增节点,
val[++tot] = v;//节点赋值
dat[tot] = rand();//随机优先级
size[tot] = 1;//目前是新建叶子节点,所以子树大小为1
cnt[tot] = 1;//新建节点同理副本数为1
return tot;
}
void pushup(int id){//和线段树的pushup更新一样
size[id] = size[ch[id][0]] + size[ch[id][1]] + cnt[id];//本节点子树大小 = 左儿子子树大小 + 右儿子子树大小 + 本节点副本数
}
void build(){
root = New(-INF),ch[root][1] = New(INF);//先加入正无穷和负无穷,便于之后操作(貌似不加也行)
pushup(root);//因为INF > -INF,所以是右子树,
}
void Rotate(int &id,int d){//id是引用传递,d(irection)为旋转方向,0为左旋,1为右旋
int temp = ch[id][d ^ 1];//旋转理解:找个动图看一看就好(或参见其他OIer的blog)
ch[id][d ^ 1] = ch[temp][d];//这里讲一个记忆技巧,这些数据都是被记录后马上修改
ch[temp][d] = id;//所以像“Z”一样
id = temp;//比如这个id,在上一行才被记录过,ch[temp][d]、ch[id][d ^ 1]也是一样的
pushup(ch[id][d]),pushup(id);//旋转以后size会改变,看图就会发现只更新自己和转上来的点,pushup一下,注意先子节点再父节点
}//旋转实质是({在满足BST的性质的基础上比较优先级}通过交换本节点和其某个叶子节点)把链叉开成二叉形状(从而控制深度),可以看图理解一下
void insert(int &id,int v){//id依然是引用,在新建节点时可以体现
if(!id){
id = New(v);//若节点为空,则新建一个节点
return ;
}
if(v == val[id])cnt[id]++;//若节点已存在,则副本数++;
else{//要满足BST性质,小于插到左边,大于插到右边
int d = v < val[id] ? 0 : 1;//这个d是方向的意思,按照BST的性质,小于本节点则向左,大于向右
insert(ch[id][d],v);//递归实现
if(dat[id] < dat[ch[id][d]])Rotate(id,d ^ 1);//(参考一下图)与左节点交换右旋,与右节点交换左旋
}
pushup(id);//现在更新一下本节点的信息
}
void Remove(int &id,int v){//最难de部分了
if(!id)return ;//到这了发现查不到这个节点,该点不存在,直接返回
if(v == val[id]){//检索到了这个值
if(cnt[id] > 1){cnt[id]--,pushup(id);return ;}//若副本不止一个,减去一个就好
if(ch[id][0] || ch[id][1]){//发现只有一个值,且有儿子节点,我们只能把值旋转到底部删除
if(!ch[id][1] || dat[ch[id][0]] > dat[ch[id][1]]){//当前点被移走之后,会有一个新的点补上来(左儿子或右儿子),按照优先级,优先级大的补上来
Rotate(id,1),Remove(ch[id][1],v);//我们会发现,右旋是与左儿子交换,当前点变成右节点;左旋则是与右儿子交换,当前点变为左节点
}
else Rotate(id,0),Remove(ch[id][0],v);
pushup(id);
}
else id = 0;//发现本节点是叶子节点,直接删除
return ;//这个return对应的是检索到值de所有情况
}
v < val[id] ? Remove(ch[id][0],v) : Remove(ch[id][1],v);//继续BST性质
pushup(id);
}
int get_rank(int id,int v){
if(!id)return 0;//若查询值不存在,返回
if(v == val[id])return size[ch[id][0]] + 1;//查询到该值,由BST性质可知:该点左边值都比该点的值(查询值)小,故rank为左儿子大小 + 1
else if(v < val[id])return get_rank(ch[id][0],v);//发现需查询的点在该点左边,往左边递归查询
else return size[ch[id][0]] + cnt[id] + get_rank(ch[id][1],v);//若查询值大于该点值。说明询问点在当前点的右侧,且此点的值都小于查询值,所以要加上cnt[id]
}
int get_val(int id,int rank){
if(!id)return INF;//一直向右找找不到,说明是正无穷
if(rank <= size[ch[id][0]])return get_val(ch[id][0],rank);//左边排名已经大于rank了,说明rank对应的值在左儿子那里
else if(rank <= size[ch[id][0]] + cnt[id])return val[id];//上一步排除了在左区间的情况,若是rank在左与中(目前节点)中,则直接返回目前节点(中区间)的值
else return get_val(ch[id][1],rank - size[ch[id][0]] - cnt[id]);//剩下只能在右区间找了,rank减去左区间大小和中区间,继续递归
}
int get_pre(int v){
int id = root,pre;//递归不好返回,以循环求解
while(id){//查到节点不存在为止
if(val[id] < v)pre = val[id],id = ch[id][1];//满足当前节点比目标小,往当前节点的右侧寻找最优值
else id = ch[id][0];//无论是比目标节点大还是等于目标节点,都不满足前驱条件,应往更小处靠近
}
return pre;
}
int get_next(int v){
int id = root,next;
while(id){
if(val[id] > v)next = val[id],id = ch[id][0];//同理,满足条件向左寻找更小解(也就是最优解)
else id = ch[id][1];//与上方同理
}
return next;
}
int main(){
build();//不要忘记初始化[运行build()会连同root一并初始化,所以很重要]
na = RD();
for(int i = 1;i <= na;i++){
int cmd = RD(),x = RD();
if(cmd == 1)insert(root,x);//函数都写好了,注意:需要递归的函数都从根开始,不需要递归的函数直接查询
else if(cmd == 2)Remove(root,x);
else if(cmd == 3)printf("%d\n",get_rank(root,x) - 1);//注意:因为初始化时插入了INF和-INF,所以查询排名时要减1(-INF不是第一小,是“第零小”)
else if(cmd == 4)printf("%d\n",get_val(root,x + 1));//同理,用排名查询值得时候要查x + 1名,因为第一名(其实不是)是-INF
else if(cmd == 5)printf("%d\n",get_pre(x));
else if(cmd == 6)printf("%d\n",get_next(x));
}
return 0;
}
复杂度
*范浩强treap
【代码实现】
//https://www.luogu.org/problemnew/show/P3369
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
struct node{
int siz,val,key,lch,rch;
}t[100005];
int tot,seed=233,root=1;
int Rand(){//随机key值
return seed=int(seed*482711ll%2147483647);
}
int NEW(int val){//新建节点
t[++tot].siz=1;
t[tot].val=val;
t[tot].key=Rand();
t[tot].lch=t[tot].rch=0;
return tot;
}
void update(int now){//维护子树大小
t[now].siz=t[t[now].lch].siz+t[t[now].rch].siz+1;
}
void split(int now,int &a,int &b,int val){//拆分操作
//now原Treap,a左树,b右树,val判定值
//注意传地址符
if(now==0){
a=b=0;//若now=0分割完毕;
return;
}
if(t[now].val<=val)//因为now左子树中的所有值都小于now的值,所以若now属于左树,那么他们都属于左树递归now的右子树;
a=now,split(t[now].rch,t[a].rch,b,val);//a=now已经使a的右子树=now的右子树,不再递归a的右子树;
else//同上now右子树也都属于左树,递归左子树;
b=now,split(t[now].lch,a,t[b].lch,val);
update(now);//因为后面会用到左(右)树的siz所以更新维护
}
void merge(int &now,int a,int b){//合并操作
//now新树
if(a==0||b==0){
now=a+b;//若某个树已空,则将另一个树整体插入
return;
}
//按照key值合并(堆性质)
if(t[a].key<t[b].key)//若a树key值<b树,那么b树属于a树的后代,因为b树恒大于a树,那么b树一定属于a树的右后代,a的左子树不变,直接赋给now,递归合并a的右子树和b
now=a,merge(t[now].rch,t[a].rch,b);
else
now=b,merge(t[now].lch,a,t[b].lch);//同理,a树一定是b树的左儿子,递归合并b的左儿子和a
update(now);
}
void find(int now,int rank){//找第k大
while(t[t[now].lch].siz+1!=rank){
if(t[t[now].lch].siz>=rank)
now=t[now].lch;//若左子树大小大于rank,找左子树
else
rank-=(t[t[now].lch].siz+1),now=t[now].rch;//找右子树(rank-左子树大小-树根(大小为1))号的元素
}
printf("%d\n",t[now].val);
}
void insert(int val){
int x=0,y=0,z;
z=NEW(val);//新建节点z,作为z树
split(root,x,y,val);//将树分为两部分,x树为<=待插入值,y树大于
merge(x,x,z);//合并x树和新节点z(树),赋给x树
merge(root,x,y);//合并新x树和y树,赋给根
//就这么简单
}
void delet(int val){
int x=0,y=0,z=0;
split(root,x,y,val);//分为x树为<=待删除,y树大于
split(x,x,z,val-1);//x树分为新x树<待删除,z树等于待删除
merge(z,t[z].lch,t[z].rch);//合并z树左右儿子,赋给z树,即丢弃z树根节点(实现删除)
merge(x,x,z);
merge(root,x,y);//合并,不在重复
}
void get_rank(int val){
int x=0,y=0;
split(root,x,y,val-1);//分为小于待查找值的x树和大于等于的y树
printf("%d\n",t[x].siz+1);//即为待查找值的编号
merge(root,x,y);//合并
}
void get_val(int rank){
find(root,rank);//find查找即可
}
void get_pre(int val){
int x=0,y=0;
split(root,x,y,val-1);//x树为<=val-1值即小于val值
find(x,t[x].siz);//在小于val值中找最大的(编号为siz的)就是前驱
merge(root,x,y);
}
void get_nxt(int val){
int x=0,y=0;
split(root,x,y,val);//x树小于等于val值,那么y树大于val值
find(y,1);//在y树中找最小的,即为后继
merge(root,x,y);//合并
}
int main(){
int i,j,k,m;
NEW(2147483627);//初始虚节点
t[1].siz=0;//siz为0,不算虚节点的大小
scanf("%d",&m);
for(i=1;i<=m;i++){
scanf("%d%d",&j,&k);
if(j==1)insert(k);
if(j==2)delet(k);
if(j==3)get_rank(k);
if(j==4)get_val(k);
if(j==5)get_pre(k);
if(j==6)get_nxt(k);
}
return 0;
}
复杂度
*替罪羊树
【简介】
替罪羊树是计算机科学中,一种基于部分重建的自平衡二叉搜索树。在替罪羊树上,插入或删除节点的平摊最坏时间复杂度是
,搜索节点的最坏时间复杂度是
。
【代码实现】
//https://www.luogu.org/problemnew/show/P3369
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define alpha 0.8
#define maxn 2000001
#define ri register
#define il inline
using namespace std;
struct scapegoat{
int son[2], val, valid, total;
bool exist;
}e[maxn];
int memory[maxn];
int cur[maxn];
int root, pool, poi, cnt, to_rebuild;
il bool isbad(int now){
if((double)e[now].valid*alpha <= (double)max(e[e[now].son[0]].valid, e[e[now].son[1]].valid)) return true;
return false;
}
void dfs(int now){
if(!now) return;
dfs(e[now].son[0]);
if(e[now].exist) cur[++poi] = now;
else memory[++pool] = now;
dfs(e[now].son[1]);
}
void build(int l, int r, int &now){
int mid = l+r>>1;
now = cur[mid];
if(l == r){
e[now].son[0] = e[now].son[1] = 0;
e[now].total = e[now].valid = 1;
return;
}
if(l < mid) build(l,mid-1,e[now].son[0]);
else e[now].son[0] = 0;
build(mid+1,r,e[now].son[1]);
e[now].total = e[e[now].son[0]].total + e[e[now].son[1]].total + 1;
e[now].valid = e[e[now].son[0]].valid + e[e[now].son[1]].valid + 1;
}
il void rebuild(int &now){
poi = 0;
dfs(now);
if(poi) build(1,poi,now);
else now = 0;
}
il int find_rank(int k) {
int now = root;
int ans = 1;
while(now){
if(e[now].val >= k) now = e[now].son[0];
else{
ans += e[e[now].son[0]].valid + e[now].exist;
now = e[now].son[1];
}
}
return ans;
}
il int find_kth(int k){
int now = root;
while(now){
if(e[now].exist&&e[e[now].son[0]].valid+1 == k) return e[now].val;
else if(e[e[now].son[0]].valid >= k) now = e[now].son[0];
else{
k -= e[e[now].son[0]].valid + e[now].exist;
now = e[now].son[1];
}
}
}
void insert(int &now, int val){
if(!now){
now = memory[pool--]; e[now].val = val;
e[now].exist = e[now].total = e[now].valid = 1;
e[now].son[0] = e[now].son[1] = 0;
return;
}
e[now].total++, e[now].valid++;
if(e[now].val >= val) insert(e[now].son[0], val);
else insert(e[now].son[1], val);
if(!isbad(now)){
if(to_rebuild){
if(e[now].son[0] == to_rebuild) rebuild(e[now].son[0]);
else rebuild(e[now].son[1]);
to_rebuild = 0;
}
}
else to_rebuild = now;
}
il void delete_pos(int &now, int tar){//target(目标)
if(e[now].exist&&e[e[now].son[0]].valid+ 1 == tar){
e[now].exist = 0; e[now].valid--; return;
}
e[now].valid--;
if(e[e[now].son[0]].valid + e[now].exist >= tar) delete_pos(e[now].son[0], tar);
else delete_pos(e[now].son[1],tar-e[e[now].son[0]].valid-e[now].exist);
}
il void delete_val(int tar){
delete_pos(root, find_rank(tar));
if((double)e[root].total*alpha > e[root].valid) rebuild(root);
}
int main(){
int opt, x, m;
for(int i = 2000000; i >= 1; i--) memory[++pool] = i;
scanf("%d",&m);
while(m--){
scanf("%d%d",&opt,&x);
if(opt == 1) {insert(root, x);}
if(opt == 2) {delete_val(x);}
if(opt == 3) {printf("%d\n",find_rank(x));}
if(opt == 4) {printf("%d\n",find_kth(x));}
if(opt == 5) {printf("%d\n",find_kth(find_rank(x)-1));}
if(opt == 6) {printf("%d\n",find_kth(find_rank(x+1)));}
}
return 0;
}
复杂度
*树套树
【代码实现】
#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <complex>
#include <string>
#include <cstdio>
#include <vector>
#include <bitset>
#include <cmath>
#include <ctime>
#include <queue>
#include <stack>
#include <map>
#include <set>
using namespace std;
const int MAXN = 1e5 + 100;
int n, m;
int a[MAXN];
namespace Treap{
struct balanced{
int w,sz,num,fix;
int ch[2];
};
int tot;
balanced tree[MAXN * 20];
int newnode(int w){
++tot;
tree[tot].w = w;
tree[tot].fix = rand();
tree[tot].num = 1;
tree[tot].ch[0] = tree[tot].ch[1] = 0;
tree[tot].sz = 1;
return tot;
}
void pushup(int p){
tree[p].sz = tree[tree[p].ch[0]].sz + tree[tree[p].ch[1]].sz + tree[p].num;
}
void rotate(int &p, int d){
int y = tree[p].ch[d];
tree[p].ch[d] = tree[y].ch[d ^ 1];
tree[y].ch[d ^ 1] = p;
pushup(p);
pushup(y);
p = y;
}
void insert(int &p, int w){
if (!p)
p = newnode(w);
else if (tree[p].w == w)
++tree[p].num;
else {
if (tree[p].w > w){
insert(tree[p].ch[0], w);
if (tree[tree[p].ch[0]].fix > tree[p].fix)
rotate(p, 0);
}
else{
insert(tree[p].ch[1], w);
if (tree[tree[p].ch[1]].fix > tree[p].fix)
rotate(p, 1);
}
}
pushup(p);
}
void remove(int &p, int w){
if (tree[p].w > w)
remove(tree[p].ch[0], w);
else if (tree[p].w < w)
remove(tree[p].ch[1], w);
else{
if (tree[p].num > 1)
--tree[p].num;
else{
if (!tree[p].ch[0] && !tree[p].ch[1])
p = 0;
else if (!tree[p].ch[0]){
rotate(p, 1);
remove(tree[p].ch[0], w);
}
else if (!tree[p].ch[1]){
rotate(p, 0);
remove(tree[p].ch[1], w);
}
else{
if (tree[tree[p].ch[0]].fix > tree[tree[p].ch[1]].fix){
rotate(p, 0);
remove(tree[p].ch[1], w);
}
else{
rotate(p, 1);
remove(tree[p].ch[0], w);
}
}
}
}
if (p)pushup(p);
}
int queryrank(int p, int k) // return the highest rank of value 'k'{
if (!p)return 0;
if (tree[p].w > k)return queryrank(tree[p].ch[0], k);
else if (tree[p].w == k)return tree[tree[p].ch[0]].sz;
else return tree[tree[p].ch[0]].sz + tree[p].num + queryrank(tree[p].ch[1], k);
}
int querynum(int p, int k) // return the value of kth rank node{
if (tree[tree[p].ch[0]].sz + 1 == k)return tree[p].w;
else if (tree[tree[p].ch[0]].sz + 1 < k)return querynum(tree[p].ch[1], k - 1 - tree[tree[p].ch[0]].sz);
else return querynum(tree[p].ch[0], k);
}
int querypre(int p, int k) // return the prefix of value k{
if (!p) return -2147483647;
if (tree[p].w >= k) return querypre(tree[p].ch[0], k);
else return max(tree[p].w, querypre(tree[p].ch[1], k));
}
int querysuf(int p, int k) // return the suffix of value k{
if (!p)return 2147483647;
if (tree[p].w <= k)return querysuf(tree[p].ch[1], k);
else return min(tree[p].w, querysuf(tree[p].ch[0], k));
}
void listall(int p){
if (tree[p].ch[0])listall(tree[p].ch[0]);
cerr << tree[p].w << ",sz=" << tree[p].num << " ";
if (tree[p].ch[1])listall(tree[p].ch[1]);
}
}
using Treap::listall;
namespace SEG{
struct segment{
int l,r;
int root;
};
segment tree[MAXN * 8];
void build(int p, int l, int r){
tree[p].l = l;
tree[p].r = r;
for (int i = l; i < r + 1; ++i)
Treap::insert(tree[p].root, a[i]);
if (l != r){
int mid = (l + r) / 2;
build(p * 2, l, mid);
build(p * 2 + 1, mid + 1, r);
}
}
void modify(int p, int x, int y){
Treap::remove(tree[p].root, a[x]);
Treap::insert(tree[p].root, y);
if (tree[p].l == tree[p].r)return;
int mid = (tree[p].l + tree[p].r) / 2;
if (x > mid)modify(p * 2 + 1, x, y);
else modify(p * 2, x, y);
}
int queryrank(int p, int l, int r, int k){ // query the highest rank of value 'k'
if (tree[p].l > r || tree[p].r < l)return 0;
if (tree[p].l >= l && tree[p].r <= r)
return Treap::queryrank(tree[p].root, k);
else return queryrank(p * 2, l, r, k) + queryrank(p * 2 + 1, l, r, k);
}
int querynum(int u, int v, int k){ // query the value of kth num
int l = 0, r = 1e8;
while (l < r){
int mid = (l + r + 1) / 2;
if (queryrank(1, u, v, mid) < k)l = mid;
else r = mid - 1;
}
return r;
}
int querypre(int p, int l, int r, int k){
if (tree[p].l > r || tree[p].r < l)
return -2147483647;
if (tree[p].l >= l && tree[p].r <= r)
return Treap::querypre(tree[p].root, k);
else return max(querypre(p * 2, l, r, k), querypre(p * 2 + 1, l, r, k));
}
int querysuf(int p, int l, int r, int k){
if (tree[p].l > r || tree[p].r < l)
return 2147483647;
if (tree[p].l >= l && tree[p].r <= r)
return Treap::querysuf(tree[p].root, k);
else return min(querysuf(p * 2, l, r, k), querysuf(p * 2 + 1, l, r, k));
}
}
inline int read(){
int x=0,f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
int main(){
n = read();
m = read();
for (int i = 1; i < n + 1; ++i)a[i] = read();
SEG::build(1, 1, n);
for (int i = 0; i < m; ++i){
int opt = read();
if (opt == 3){
int x = read(), y = read();
SEG::modify(1, x, y);
a[x] = y;
}
else{
int l = read(), r = read(), k = read();
if (opt == 1) printf("%d\n", SEG::queryrank(1, l, r, k) + 1);
else if (opt == 2) printf("%d\n", SEG::querynum(l, r, k));
else if (opt == 4) printf("%d\n", SEG::querypre(1, l, r, k));
else printf("%d\n", SEG::querysuf(1, l, r, k));
}
}
return 0;
}
*主席树
好像写过了
不管
换个名字再来一遍
【代码实现】
//https://www.luogu.org/problemnew/show/P3834
#include<iostream>
#include<cstdio>
#include<ctype.h>
#include<algorithm>
using namespace std;
inline int read(){
int x=0,f=0;char ch=getchar();
while(!isdigit(ch))f|=ch=='-',ch=getchar();
while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
struct Tree{
int lc,rc,ans;
}tree[3600007];
int rt[200007],a[200007],s[200007],tot;
void insert(int &root,int last,int l,int r,int k){
root=++tot;
tree[root].lc=tree[last].lc;
tree[root].rc=tree[last].rc;
tree[root].ans=tree[last].ans+1;
if(l==r) return ;
int mid=l+r>>1;
if(k<=mid)insert(tree[root].lc,tree[last].lc,l,mid,k);
else insert(tree[root].rc,tree[last].rc,mid+1,r,k);
}
int quary(int L,int R,int l,int r,int k){
if(l==r) return l;
int mid=l+r>>1,o=tree[tree[R].lc].ans-tree[tree[L].lc].ans;
if(k<=o) return quary(tree[L].lc,tree[R].lc,l,mid,k);
else return quary(tree[L].rc,tree[R].rc,mid+1,r,k-o);
}
int main(){
int n=read(),m=read();
for(int i=1;i<=n;++i)s[i]=a[i]=read();
sort(s+1,s+1+n);
int len=unique(s+1,s+n+1)-s-1;
for(int i=1;i<=n;++i){
int o=lower_bound(s+1,s+len+1,a[i])-s;
insert(rt[i],rt[i-1],1,len,o);
}
while(m--){
int l=read(),r=read(),k=read();
printf("%d\n",s[quary(rt[l-1],rt[r],1,len,k)]);
}
return 0;
}
*LCT(Link-Cut Tree)
【简介】
是一种解决动态树问题的方法,由
(为什么又是他) 在
年提出,最原始的论文在这里,在论文中,
除了介绍了均摊复杂度为
的LCT之外,还介绍了一种时间复杂度为严格
的算法。
【代码实现】
//https://www.luogu.org/problemnew/show/P3690
#include<bits/stdc++.h>
#define N 300005
using namespace std;
int n,m,val[N];
struct Link_Cut_Tree{
int top,c[N][2],fa[N],xr[N],q[N],rev[N];
inline void pushup(int x){
xr[x]=xr[c[x][0]]^xr[c[x][1]]^val[x];
}
inline void pushdown(int x){
int l=c[x][0],r=c[x][1];
if(rev[x]){
rev[l]^=1;
rev[r]^=1;
rev[x]^=1;
swap(c[x][0],c[x][1]);
}
}
inline bool isroot(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void rotate(int x){
int y=fa[x],z=fa[y],l,r;
if(c[y][0]==x)l=0;
else l=1;
r=l^1;
if(!isroot(y)){
if(c[z][0]==y)c[z][0]=x;
else c[z][1]=x;
}
fa[x]=z;
fa[y]=x;
fa[c[x][r]]=y;
c[y][l]=c[x][r];
c[x][r]=y;
pushup(y);
pushup(x);
}
void splay(int x){
top=1;
q[top]=x;
for(int i=x; !isroot(i); i=fa[i])q[++top]=fa[i];
for(int i=top; i; i--)pushdown(q[i]);
while(!isroot(x)){
int y=fa[x],z=fa[y];
if(!isroot(y)){
if((c[y][0]==x)^(c[z][0]==y))rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
for(int t=0; x; t=x,x=fa[x])splay(x),c[x][1]=t,pushup(x);
}
void makeroot(int x){
access(x);
splay(x);
rev[x]^=1;
}
int find(int x){
access(x);
splay(x);
while(c[x][0])x=c[x][0];
return x;
}
void split(int x,int y){
makeroot(x);
access(y);
splay(y);
}
void cut(int x,int y){
split(x,y);
if(c[y][0]==x)c[y][0]=0,fa[x]=0;
}
void link(int x,int y){
makeroot(x);
fa[x]=y;
}
} T;
inline int read(){
int f=1,x=0;
char ch;
do{
ch=getchar();
if(ch=='-')f=-1;
}
while(ch<'0'||ch>'9');
do{
x=x*10+ch-'0';
ch=getchar();
}
while(ch>='0'&&ch<='9');
return f*x;
}
int main(){
n=read();
m=read();
for(int i=1; i<=n; i++)val[i]=read(),T.xr[i]=val[i];
while(m--){
int opt=read();
if(opt==0){
int x=read(),y=read();
T.split(x,y);
printf("%d\n",T.xr[y]);
}
else if(opt==1){
int x=read(),y=read(),xx=T.find(x),yy=T.find(y);
if(xx!=yy)T.link(x,y);
}
esle if(opt==2){
int x=read(),y=read(),xx=T.find(x),yy=T.find(y);
if(xx==yy)T.cut(x,y);
}
else if(opt==3){
int x=read(),y=read();
T.access(x);
T.splay(x);
val[x]=y;
T.pushup(x);
}
}
return 0;
}
复杂度