ACM常用模板-数据结构
数据结构方面常用模板总结,大多数代码摘自网络,个人整理总结
string:
任意进制转换:
itoa(int n,char* s,int r) //将10进制n转换为r进制并赋给s
流:
#include<sstream>
stringstream stream; //创建名为stream的流
stream.clear //重复使用必须清空
string a,str;
stream(a); //将字符串a放入流
while(a>>str) //将a流入str中,以空格为拆分进行输出
cout<<str<<endl;
迭代器:
string::iterator it //创建名为it的迭代器
反转:
reverse(s.begin(), s.end()); //原地反转
s1.assign(s.rbegin(), s.rend()); //反转并赋给s1
大小写转换:
transform(s.begin(), s.end(), s.begin(), ::toupper);
transform(s.begin(), s.end(), s.begin(), ::tolower);
类型转换:
string ->int : string s("123");
int i = atoi(s.c_str());
int -> string: int a;
stringstream(s) >> a;
子串:
string substr(int pos = 0,int n = npos) const;//返回pos开始的n个字符组成的字符串
更改:
s.assign(str); //直接
s.assign(str,1,3);//如果str是”iamangel” 就是把”ama”赋给字符串
s.assign(str,2,string::npos);//把字符串str从索引值2开始到结尾赋给s
s.assign(“gaint”); //不说
s.assign(“nico”,5);//把’n’ ‘I’ ‘c’ ‘o’ ‘\0’赋给字符串
s.assign(5,’x’);//把五个x赋给字符串
删除:
s.erase(13);//从索引13开始往后全删除
s.erase(7,5);//从索引7开始往后删5个
iterator erase(iterator it);//删除it指向的字符,返回删除后迭代器的位置
iterator erase(iterator first, iterator last);//删除[first,last)之间的所有字符,返回删除后迭代器的位置
查找:
int find(char c, int pos = 0) const;//从pos开始查找字符c在当前字符串的位置
int find(const char *s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置
int find(const char *s, int pos, int n) const;//从pos开始查找字符串s中前n个字符在当前串中的位置
int find(const string &s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置。
删除所有特定字符:
str.erase(std::remove(str.begin(), str.end(), 'a'), str.end());
集合set:
头文件:#include<set>
声明:set<T>s;
迭代器:set<T>::iterator;
插入:A.insert("Tom");
删除:A.erase("Tom"); 只支持正向迭代器
查找:A.count("Tom");
遍历:set<string>::iterator it;
for(it=A.begin();it!=A.end();it++)
cout<<(*it);
正向:
A.begin(); 返回集合中第一个元素的迭代器
A.end(); 返回集合中最后一个元素下一位置的迭代器
反向:
set<T>:: reverse_iterator;
A.rbegin(); 返回集合中最后一个元素的反向迭代器
A.rend(); 返回集合中第一个元素前一位置的反向迭代器
清空:A.clear();
取并:set_union(A.begin(),A.end(),B.begin(),B.end(),inserter( C , C.begin() ) ); 将A集合与B集合取并后存入C集合,要去A,B必须有序
取交:set_intersection(A.begin(),A.end(),B.begin(),B.end(),inserter( C , C.begin() )); 取交集,同上
映射map:
头文件:#include<map>
声明:map<T1,T2>m;
插入:A.insert(pari<string,int>("Tom",1));
A["Tom"]=1;
访问:cout<<A["Tom"];
查找:A.count("Tom")
若存在返回1,否则返回0
遍历:map<string,int>::iterator it;
for(it=A.begin();it!=A.end();it++)
cout<<it->first<<it->second;
清空:A.clear();
删除:A.erase(it);
优先队列:
头文件:#include<queue>
默认从大到小排列
声明:
普通优先队列:priority_queue<int>Q;
从小到大优先队列:priority_queue<int,vector<int>,greater<int> >Q;
对结构体应用优先队列:
struct Node
{
int x,y,val;
friend bool operator < (node n1,node n2)
{
if(n1.val==n2.val)
return n1.x>n2.x;
return n1.val<n2.val; //和正常不同,"<"为从大到小,">"为从小到大
}
};
priority_queue<node>Q
单调栈:
https://blog.csdn.net/wubaizhe/article/details/70136174
利用单调栈,可以找到从左/右遍历第一个比它小/大的元素的位置.由此也可知道该区间上数的个数O(n)
可统计出a[i]~a[n],以i为起点的单调序列长度,从后向前建立单调栈,常做预处理用
一个元素向左遍历的第一个比它小的数的位置就是将它插入单调栈时栈顶元素的值,若栈为空,则说明不存在这么一个数。然后将此元素的下标存入栈,就能类似迭代般地求解后面的元素
#include <iostream>
#include <stack>
using namespace std;
stack<int>s;
int n;
int ans[N]; //ans[i],表示第i个元素,向左遍历,第一个比它小的元素的位置
void getans(int a[])
{
for(int i=1;i<=n;i++)
{
while(s.size() && a[s.top()]>=a[i]) s.pop();
if(s.empty()) ans[i]=0;
else ans[i]=s.top();
s.push(i);
}
}
单调队列:
整理归纳单调队列的定义:
1、维护区间最值;
2、去除冗杂状态;
3、保持队列单调(最大值是单调递减序列,最小值是单调递增序列);
4、最优选择在队首。
在维护好一个 区间正确、严格递减 的单调递减队列后,队列头就是当前区间的最大值了
整理归纳单调队列的使用方法:
1、维护队首(对于上题就是如果你已经是当前的m个之前那你就可以被删了) ;
2、在队尾插入(每插入一个就要从队尾开始往前去除冗杂状态) ;
3、取出需要的最优解(队列头的值即是);
4、借助最优解,得到目前所求的最优解(通常此处插入DP方程)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[200000];
struct node
{
int x,p;
node(){}
node(int xx,int pp){x=xx;p=pp;}
}list[200000];
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int head=1,tail=1;
list[1]=node(a[1],1);
for(int i=2;i<=n;i++)
{
while(head<=tail&&list[tail].x<=a[i]) tail--;//删尾
list[++tail]=node(a[i],i);//得到最优解并插入
while(i-list[head].p>=m) head++;//去头
if(i>=m) printf("%d\n",list[head]);
}
return 0;
}
二分查找:
只能对有序数列使用,内部通过二分查找实现
lower_bound(a,a+n,m)://返回数组a中,0~n里第一个大于等于m的指针
int pos=lower_bound(a,a+n,m)-a://返回数组a中,0~n里第一个大于等于m的位置
upper_bound(a,a+n,m)://返回数组a中,0~n里第一个大于m的指针
int pos=upper_bound(a,a+n,m)-a://返回数组a中,0~n里第一个大于m的位置
lower_bound(seq2, seq2+6, 7, greater<int>())://加greater<int>()后,lower变小于等于,upper变小于
排列:
头文件:#include<algorithm>
next_permutation(a,a+3) //求数组a中前3个元素的下一个排列
prev_permutation(a,a+3) //求数组a中前3个元素的前一个排列
a可为普通数组,string,vector......
求1-10的第10个排列
a[10]={1,2,3,4,5,6,7,8,9,10};
for(int i=1;i<k;i++)
next_permutation(a,a+10);
for(int i=0;i<10;i++)
cout<<a[i]<<" ";
树状数组:
巧妙利用二进制性质,实现在log(n)对区间和的查询,修改操作
#include <iostream>
#include <string.h>
using namespace std;
const int N=100050;
int c[N],ans[N]; //c[n]表示a[1~n]的和,a数组省略
int lowbit(int x) //求2^k
{
return x & -x;
}
int getsum(int n) //区间查询,求a[1~n]的和
{
int res = 0;
while(n>0)
{
res+=c[n];
n=n-lowbit(n);
}
return res;
}
int change(int x) //单点更新,将a[x]的值加1
{
while(x<=N)
{
c[x]++;
x+=lowbit(x);
}
}
int main()
{
int n;
cin>>n;
memset(c,0,sizeof(c));
memset(ans,0,sizeof(ans));
for(int i=0;i<n;i++)
{
int x,y;
cin>>x>>y;
x++;
ans[getsum(x)]++;
change(x);
}
for(int i=0;i<n;i++)
cout<<ans[i]<<endl;
return 0;
}
线段树:
单点操作,区间查询:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
using namespace std;
#define INF 10000000
#define lson l,mid,rt<<1 //左儿子
#define rson mid+1,r,rt<<1|1 //右儿子
const int maxn = 222222;
struct Node{
int Max,Min; //区间的最大值和最小值
int sum; //区间的和
}stree[maxn<<2];
void up(int rt){ //更新该区间的最值与和
stree[rt].Max=max(stree[rt<<1].Max,stree[rt<<1|1].Max);
stree[rt].Min=min(stree[rt<<1].Min,stree[rt<<1|1].Min);
stree[rt].sum=stree[rt<<1].sum+stree[rt<<1|1].sum;
}
void build(int l,int r,int rt){ //在结点i上建立区间为[l,r]
if(l==r){ //叶子结点
int num;
scanf("%d",&num);
stree[rt].Max=stree[rt].Min=stree[rt].sum=num;
return ;
}
int mid=(l+r)>>1;
build(lson); //建立左儿子
build(rson); //建立右儿子
up(rt); //更新
}
int querymax(int a,int b,int l,int r,int rt){ //求区间[a,b]的最大值
if(a<=l&&r<=b){ //如果全包含,直接取区间最大值
return stree[rt].Max;
}
int mid = (r+l)>>1;
int ret = -INF;
if(a<=mid) ret=max(ret,querymax(a,b,lson));//如果左端点在中点的左边,找出左区间的最大值
if(mid<b) ret=max(ret,querymax(a,b,rson));//如果右端点在中点的右边,找出右区间(以及左区间)的最大值
return ret;
}
int querymin(int a,int b,int l,int r,int rt){ //求区间[a,b]的最小值
if(a<=l&&r<=b){ //如果全包含,直接取区间最小值
return stree[rt].Min;
}
int mid = (r+l)>>1;
int ret = INF;
if(a<=mid) ret=min(ret,querymin(a,b,lson));//如果左端点在中点的左边,找出左区间的最小值
if(mid<b) ret=min(ret,querymin(a,b,rson)); //如果右端点在中点的右边,找出右区间(以及左区间)的最小值
return ret;
}
int querysum(int a,int b,int l,int r,int rt){ //求区间[a,b]的和(a,b的值相同时为求单点的值)
if(a<=l&&r<=b){ //如果全包含,直接取区间的和
return stree[rt].sum;
}
int mid = (r+l)>>1;
int ret=0;
if(a<=mid) ret+=querysum(a,b,lson);
if(mid<b) ret+=querysum(a,b,rson);
return ret;
}
void uppoint(int a,int b,int l,int r,int rt){ //单点替换,把第a个数换成b
if(l==r){
stree[rt].Max=stree[rt].Min=stree[rt].sum=b;
return ;
}
int mid =(r+l)>>1;
if(a<=mid)uppoint(a,b,lson);
else uppoint(a,b,rson);
up(rt);
}
void upadd(int a,int b,int l,int r,int rt){ //单点增减,把第a个数增减b
if(l==r){
stree[rt].sum=stree[rt].sum+b;
stree[rt].Max=stree[rt].Max+b;
stree[rt].Min=stree[rt].Min+b;
return ;
}
int mid=(l+r)>>1;
if(a<=mid) upadd(a,b,lson);
else upadd(a,b,rson);
up(rt);
}
int main()
{
//freopen("F:\\11.txt","r",stdin);
int n,q;
while(~scanf("%d%d",&n,&q)){
build(1,n,1);//build(l,r,rt);
while(q--){
char op[10];
int a,b;
scanf("%s%d%d",op,&a,&b);
if(op[0]=='X'){//求区间[a,b]的最大值
printf("%d\n",querymax(a,b,1,n,1));//querymax(int a,int b,int l,int r,int rt);
}
else if(op[0]=='N'){//求区间[a,b]的最小值
printf("%d\n",querymin(a,b,1,n,1));//querymin(int a,int b,int l,int r,int rt);
}
else if(op[0]=='U'){//单点替换,把第a个数换成b
uppoint(a,b,1,n,1);//uppoint(int a,int b,int l,int r,int rt);
}
else if(op[0]=='S'){//求区间[a,b]的和(a,b的值相同时为求单点的值)
printf("%d\n",querysum(a,b,1,n,1));//querysum(int a,int b,int l,int r,int rt);
}
else if(op[0]=='A'){//单点增加,把第a个数增加b
upadd(a,b,1,n,1);
}
else if(op[0]=='E'){//单点减少,把第a个数减少b
upadd(a,-b,1,n,1);
}
}
}
return 0;
}
区间替换,区间查询:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define max(a,b) (a>b)?a:b
#define min(a,b) (a>b)?b:a
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 100100;
const int INF=0x7fffffff;
using namespace std;
int lazy[maxn<<2];
int MAX[maxn<<2];
int MIN[maxn<<2];
int SUM[maxn<<2];
void PushUp(int rt) { //由左孩子、右孩子向上更新父节点
SUM[rt] = SUM[rt<<1] + SUM[rt<<1|1];
MAX[rt] = max(MAX[rt<<1],MAX[rt<<1|1]);
MIN[rt] = min(MIN[rt<<1],MIN[rt<<1|1]);
}
void PushDown(int rt,int m) { //向下更新
if (lazy[rt]) { //懒惰标记
lazy[rt<<1] = lazy[rt<<1|1] = lazy[rt];
SUM[rt<<1] = (m - (m >> 1)) * lazy[rt];
SUM[rt<<1|1] = ((m >> 1)) * lazy[rt];
MAX[rt<<1]=MAX[rt<<1|1]=lazy[rt];
MIN[rt<<1]=MIN[rt<<1|1]=lazy[rt];
lazy[rt] = 0;
}
}
//所有的l,r,rt 带入1,n,1
void build(int l,int r,int rt) { //初始化建树
lazy[rt] = 0;
if (l== r) {
SUM[rt]=MAX[rt]=MIN[rt]=0; //初始化为0的建树
/*scanf("%d",&SUM[rt]); //边读入边建树的方法
MAX[rt]=MIN[rt]=SUM[rt];
*/
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
PushUp(rt);
}
void update(int L,int R,int v,int l,int r,int rt) { //将L~R区间的值置为v
//if(L>l||R>r) return;
if (L <= l && r <= R) {
lazy[rt] = v;
SUM[rt] = v * (r - l + 1);
MIN[rt] = v;
MAX[rt] = v;
//printf("%d %d %d %d %d\n", rt, sum[rt], c, l, r);
return ;
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
if (L <= m) update(L , R , v , lson);
if (R > m) update(L , R , v , rson);
PushUp(rt);
}
int querySUM(int L,int R,int l,int r,int rt) { //求区间L~R的和
if (L <= l && r <= R) {
//printf("%d\n", sum[rt]);
return SUM[rt];
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
int ret = 0;
if (L <= m) ret += querySUM(L , R , lson);
if (m < R) ret += querySUM(L , R , rson);
return ret;
}
int queryMIN(int L,int R,int l,int r,int rt) { //求区间L~R的最小值
if (L <= l && r <= R) {
//printf("%d\n", sum[rt]);
return MIN[rt];
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
int ret = INF;
if (L <= m) ret = min(ret, queryMIN(L , R , lson));
if (m < R) ret = min(ret,queryMIN(L , R , rson));
return ret;
}
int queryMAX(int L,int R,int l,int r,int rt) { //求区间L~R的最大值
if (L <= l && r <= R) {
//printf("%d\n", sum[rt]);
return MAX[rt];
}
PushDown(rt , r - l + 1);
int m = (l + r) >> 1;
int ret = -INF;
if (L <= m) ret = max(ret, queryMAX(L , R , lson));
if (m < R) ret = max(ret,queryMAX(L , R , rson));
return ret;
}
int main() {
int n , m;
char str[5];
while(scanf("%d%d",&n,&m)) {
build(1 , n , 1);
while (m--) {
scanf("%s",str);
int a , b , c;
if(str[0]=='T') {
scanf("%d%d%d",&a,&b,&c);
update(a , b , c , 1 , n , 1);
} else if(str[0]=='Q') {
scanf("%d%d",&a,&b);
cout<<querySUM(a,b,1,n,1)<<endl;
} else if(str[0]=='A') {
scanf("%d%d",&a,&b);
cout<<queryMAX(a,b,1,n,1)<<endl;
} else if(str[0]=='I') {
scanf("%d%d",&a,&b);
cout<<queryMIN(a,b,1,n,1)<<endl;
}
}
}
return 0;
}
区间增加,区间查询:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define max(a,b) (a>b)?a:b
#define min(a,b) (a>b)?b:a
#define lson l , m , rt << 1
#define rson m + 1 , r , rt << 1 | 1
const int maxn = 100100;
const int INF=0x7fffffff;
using namespace std;
int lazy[maxn<<2];
int SUM[maxn<<2],MAX[maxn<<2],MIN[maxn<<2];
void putup(int rt) {
SUM[rt] = SUM[rt<<1] + SUM[rt<<1|1];
MAX[rt] =max(MAX[rt<<1],MAX[rt<<1|1]) ;
MIN[rt] =min(MIN[rt<<1],MIN[rt<<1|1]);
}
void putdown(int rt,int m) {
if (lazy[rt]) {
lazy[rt<<1] += lazy[rt];
lazy[rt<<1|1] += lazy[rt];
SUM[rt<<1] += lazy[rt] * (m - (m >> 1));
SUM[rt<<1|1] += lazy[rt] * (m >> 1);
MAX[rt<<1]+=lazy[rt];
MAX[rt<<1|1] +=lazy[rt];
MIN[rt<<1]+=lazy[rt];
MIN[rt<<1|1]+=lazy[rt];
lazy[rt] = 0;
}
}
//以下的 l,r,rt 都带入 1,n,1
void build(int l,int r,int rt) { //初始化建树
lazy[rt] = 0;
if (l == r) {
//初始化树为0的写法
SUM[rt]=MAX[rt]=MIN[rt]=0;
/* //边读入边建树的写法
scanf("%d",&SUM[rt]);
MAX[rt]=MIN[rt]=SUM[rt];
*/
return ;
}
int m = (l + r) >> 1;
build(lson);
build(rson);
putup(rt);
}
void update(int L,int R,int v,int l,int r,int rt) { //将区间L~R的值增加v
if (L <= l && r <= R) {
lazy[rt] += v;
SUM[rt] += v * (r - l + 1);
MAX[rt]+=v;
MIN[rt]+=v;
return ;
}
putdown(rt , r - l + 1);
int m = (l + r) >> 1;
if (L <= m) update(L , R , v , lson);
if (m < R) update(L , R , v , rson);
putup(rt);
}
int querySUM(int L,int R,int l,int r,int rt) { //求区间L~R的和
if (L <= l && r <= R) {
return SUM[rt];
}
putdown(rt , r - l + 1);
int m = (l + r) >> 1;
int ret = 0;
if (L <= m) ret += querySUM(L , R , lson);
if (m < R) ret += querySUM(L , R , rson);
return ret;
}
int queryMAX(int L,int R,int l,int r,int rt) { //求区间L~R的最大值
if (L <= l && r <= R) {
return MAX[rt];
}
putdown(rt , r - l + 1);
int m = (l + r) >> 1;
int ret = -INF;
if (L <= m) ret =max(ret,queryMAX(L , R , lson)) ;
if (m < R) ret =max(ret,queryMAX(L , R , rson)) ;
return ret;
}
int queryMIN(int L,int R,int l,int r,int rt) { //求区间L~R的最小值
if (L <= l && r <= R) {
return MIN[rt];
}
putdown(rt , r - l + 1);
int m = (l + r) >> 1;
int ret = INF;
if (L <= m) ret = min(ret,queryMIN(L , R , lson));
if (m < R) ret = min(ret,queryMIN(L , R , rson));
return ret;
}
int main() {
int n , m;
int a , b , c;
char str[5];
scanf("%d%d",&n,&m);
build(1 , n , 1);
while (m--) {
scanf("%s",str);
if (str[0] == 'S') {
scanf("%d%d",&a,&b);
printf("%d\n",querySUM(a , b , 1 , n , 1));
} else if(str[0]=='C') {
scanf("%d%d%d",&a,&b,&c);
update(a , b , c , 1 , n , 1);
} else if(str[0]=='A') {
scanf("%d%d",&a,&b);
printf("%d\n",queryMAX(a , b , 1 , n , 1));
} else if(str[0]=='I') {
scanf("%d%d",&a,&b);
printf("%d\n",queryMIN(a , b , 1 , n , 1));
}
}
return 0;
}
区间求和
#include <stdio.h>
#include <iostream>
#define N 111111
#define LL long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
using namespace std;
LL add[N<<2];
LL sum[N<<2];
void PushUP(int rt)
{
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void PushDown(int rt,int m)
{
if(add[rt])
{
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
sum[rt<<1]+=(m-(m>>1))*add[rt];
sum[rt<<1|1]+=(m>>1)*add[rt];
add[rt]=0;
}
}
void Build(int l,int r,int rt)
{
add[rt]=0;
if(l==r)
{
scanf("%lld",&sum[rt]);
return;
}
int m=(l+r)>>1;
Build(lson);
Build(rson);
PushUP(rt);
}
void Update(int L,int R,int c,int l,int r,int rt)
{
if(L<=l&&R>=r)
{
add[rt]+=c;
sum[rt]+=(LL)c*(r-l+1);
return;
}
PushDown(rt,r-l+1);
int m=(l+r)>>1;
if(L<=m)
Update(L,R,c,lson);
if(R>m)
Update(L,R,c,rson);
PushUP(rt);
}
LL Query(int L,int R,int l,int r,int rt)
{
if(L<=l&&R>=r)
return sum[rt];
PushDown(rt,r-l+1);
int m=(l+r)>>1;
LL ret=0;
if(L<=m) ret+=Query(L,R,lson);
if(R>m) ret+=Query(L,R,rson);
return ret;
}
int main()
{
int m,n;
scanf("%d%d",&n,&m);
Build(1,n,1);
while(m--)
{
char s[5];
int a,b,c;
scanf("%s",s);
if(s[0]=='Q')
{
scanf("%d%d",&a,&b);
printf("%lld\n",Query(a,b,1,n,1));
}
else
{
scanf("%d%d%d",&a,&b,&c);
Update(a,b,c,1,n,1);
}
}
return 0;
}
zkw线段树:
代码简单,效率高
单点操作,区间查询:
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
const int N=50005;
int n,M,q;
int d[N*4];
void build(int n)
{
for(M=1;M<n;M<<=1);
for(int i=M+1;i<=M+n;i++)
scanf("%d",&d[i]);
for(int i=M-1;i;i--)
d[i]=d[i<<1]+d[i<<1|1];
//d[i]=max(d[i<<1],d[i<<1|1]); 求最值
}
void update_add(int x,int y)
{
d[x+=M]+=y;
x>>=1;
for(;x;x>>=1)
d[x]=d[x<<1]+d[x<<1|1];
}
void update_sub(int x,int y)
{
d[x+=M]-=y;
x>>=1;
for(;x;x>>=1)
d[x]=d[x<<1]+d[x<<1|1];
}
void update_change(int x,int y)
{
d[x+=M]=y;
for(x>>=1;x;x>>=1)
d[x]=max(d[x<<1],d[x<<1|1]);
}
int query_max(int s,int t)
{
int ans=-1;
for(s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1)
{
if(~s&1)
ans=max(ans,d[s^1]);
if(t&1)
ans=max(ans,d[t^1]);
}
return ans;
}
int query_sum(int s,int t)
{
int ans=0;
for(s+=M-1,t+=M+1;s^t^1;s>>=1,t>>=1)
{
if(~s&1)
ans+=d[s^1];
if(t&1)
ans+=d[t^1];
}
return ans;
}
int main()
{
int a,b;
string str;
cin>>n;
build(n);
while(cin>>str)
{
if(str=="End")
break;
if(str=="Query")
{
cin>>a>>b;
cout<<query(a,b)<<endl;
}
if(str=="Add")
{
cin>>a>>b;
update_add(a,b);
}
if(str=="Sub")
{
cin>>a>>b;
update_sub(a,b);
}
}
return 0;
}
主席树:
每个结点都是一棵线段树,维护[1~i]的前缀信息,各操作均为O(logn)
特点:查询区间第K大;将当前版本返回之前某版本;在之前某版本上区间查询
https://blog.csdn.net/creatorx/article/details/75446472
https://blog.csdn.net/CillyB/article/details/75912440
用主席树求区间第K大问题,包含离散化:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 1e5 + 10;
int n, m;
int cnt;
struct node{
int L, R;//分别指向左右子树
int sum;//该节点所管辖区间范围内数的个数
node(){
sum = 0;
}
}Tree[maxn * 20];
struct value{
int x;//值的大小
int id;//离散之前在原数组中的位置
}Value[maxn];
bool cmp(value v1, value v2)
{
return v1.x < v2.x;
}
int root[maxn];//多颗线段树的根节点
int rank[maxn];//原数组离散之后的数组
void init()
{
cnt = 1;
root[0] = 0;
Tree[0].L = Tree[0].R = Tree[0].sum = 0;
}
void update(int num, int &rt, int l, int r)
{
Tree[cnt++] = Tree[rt];
rt = cnt - 1;
Tree[rt].sum++;
if(l == r) return;
int mid = (l + r)>>1;
if(num <= mid) update(num, Tree[rt].L, l, mid);
else update(num, Tree[rt].R, mid + 1, r);
}
int query(int i, int j, int k, int l, int r) //查询第K小的数
{
int d = Tree[Tree[j].L].sum - Tree[Tree[i].L].sum;
if(l == r) return l;
int mid = (l + r)>>1;
if(k <= d) return query(Tree[i].L, Tree[j].L, k, l, mid);
else return query(Tree[i].R, Tree[j].R, k - d, mid + 1, r);
}
int query_lessKNum(int i,int j,int k,int l,int r) //查询小于等于k的数有几个
{
if(l==r) return Tree[j].sum - Tree[i].sum;
int mid=(l+r)>>1;
int res=0;
if(k<=mid) res+=query_lessKNum(Tree[i].L,Tree[j].L,k,l,mid);
else
{
res+=Tree[Tree[j].L].sum-Tree[Tree[i].L].sum;
res+=query_lessKNum(Tree[i].R,Tree[j].R,k,mid+1,r);
}
return res;
}
int main()
{
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++)
{
scanf("%d", &Value[i].x);
Value[i].id = i;
}
//进行离散化
sort(Value + 1, Value + n + 1, cmp);
for(int i = 1; i <= n; i++)
{
rank[Value[i].id] = i;
}
init();
for(int i = 1; i <= n; i++)
{
root[i] = root[i - 1];
update(rank[i], root[i], 1, n);
}
int left, right, k;
for(int i = 1; i <= m; i++)
{
scanf("%d%d%d", &left, &right, &k);
printf("%d\n", Value[query(root[left - 1], root[right], k, 1, n)].x);
}
return 0;
}
并查集:
int fa[N],rank[N];
void init(int n) //初始化
{
for(int i=0;i<n;i++)
{
fa[i]=i;
rank[i]=0;
}
}
int find(int x)
{
if(fa[x]==x)
return x;
else
return fa[x]=find(fa[x]);
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x==y) return ;
if(rank[x]<rank[y])
fa[x]=y;
else
{
fa[y]=x;
if(rank[x]==rank[y])
rank[x]++;
}
}
bool same(int x,int y)
{
return find(x)==find(y);
}
映射二叉堆:
利用set实现,可以在log(n)的时间进行增,删,改,查操作,利用优先级进行排序,再映射到相应内容
#define PII pair<int, int>
set<PII, greater<PII>> gheap; // 定义了一个大根堆
set<PII, less<PII>> lheap; // 定义了一个小根堆
//pair的first储存关键字(优先级),second储存索引或内容
int keys[MAX_INDEX]; // 存储每个索引对应的内容,如果索引的范围很大,可以用 map<int, int> 来储存
//堆的插入
gheap.insert(make_pair(value, id));
//取堆顶元素
set<PII, greater<PII>>::iterator iter = gheap.begin();
cout << iter->first << " " << iter->second << endl; // 第一个数是堆顶元素的关键字,第二个数是堆顶元素的索引
//取堆尾元素
set<PII, greater<PII>>::reverse_iterator riter = gheap.rbegin();
cout<< riter->first<<" "<<riter->second<<endl;
//删除指定元素
gheap.erase(make_pair(keys[idx], idx));
//删除堆顶元素
gheap.erase(*(gheap.begin()));
//删除堆尾元素
riter=gheap.rbegin();
int a=riter->first;
int b=riter->second;
gheap.erase(make_pair(a,b));
平衡树:
用于动态维护数据,可求特定值的排名,特定排名的值,某值的前驱,某值的后继
Treap(树堆):
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
struct TREAP{
int l,r,val,sz,recy,rd;
//sz表示树的节点数,recy记录自己被重复了几次
//rd表示该节点的优先级
}t[1000000];
int m,size,root,ans;
void update(int k){
t[k].sz=t[t[k].l].sz+t[t[k].r].sz+t[k].recy;
//更新维护
}
void left_rotate(int &k){
int y=t[k].r;t[k].r=t[y].l;t[y].l=k;
t[y].sz=t[k].sz;update(k);k=y;
//左旋,至于这里的k=y,由于下面的递归调用,
//它会一直迭代,所以无需担心会有什么错误
}
void right_rotate(int &k){
int y=t[k].l;t[k].l=t[y].r;t[y].r=k;
t[y].sz=t[k].sz;update(k);k=y;
//右旋
}
//以下函数的调用中(int k)表示在根为k的子树中
void insert(int &k,int x){//插入操作
if (k==0){//无节点时特判,
//或是递归的边界,即插入叶节点
++size;k=size;t[k].sz=t[k].recy=1;
t[k].val=x;t[k].rd=rand();return ;
//rand()生成随机的优先级,保证了期望复杂度
}
++t[k].sz;//每次向下找同时增加该节点1个节点数
if (t[k].val==x) ++t[k].recy;
//如果是相同数字,只需++recy即可
else if (x>t[k].val){
insert(t[k].r,x);
if (t[t[k].r].rd<t[k].rd) left_rotate(k);
//插入后如果违反堆性质,就进行上浮
}else{
insert(t[k].l,x);
if (t[t[k].l].rd<t[k].rd) right_rotate(k);
}
}
void del(int &k,int x){
if (k==0) return ;//无节点就跳出
if (t[k].val==x){
if (t[k].recy>1){
--t[k].recy;--t[k].sz;return ;
//如果重复了,只需--recy即可
}
if (t[k].l*t[k].r==0) k=t[k].l+t[k].r;
//如果左右儿子有为空的情况
//或将其变为其儿子节点,或将其删除
else if (t[t[k].l].rd<t[t[k].r].rd)
right_rotate(k),del(k,x);
//如果其左右儿子都有,选择优先级较大的,
//保持以后的堆性质,同时将k节点下沉
else left_rotate(k),del(k,x);
}
else if (x>t[k].val)
--t[k].sz,del(t[k].r,x);
//如果关键值不同,继续向下找
else --t[k].sz,del(t[k].l,x);
}
int atrank(int k,int x){//寻找值为x的数的排名
if (k==0) return 0;
if (t[k].val==x) return t[t[k].l].sz+1;
//如果找的关键字,根据BST的性质,
//则其排名为左子树的大小+1
else if (x>t[k].val)
return t[t[k].l].sz+t[k].recy+atrank(t[k].r,x);
//加上前面所有比它小的数,在右子树中找
else return atrank(t[k].l,x);
//如果在左子树中找的话就不用加了
}
int rerank(int k,int x){//寻找排名为x的数值
if (k==0) return 0;
if (x<=t[t[k].l].sz) return rerank(t[k].l,x);
//如果x小于了左子树的大小,那解一定在左子树中
else if (x>t[t[k].l].sz+t[k].recy)
return rerank(t[k].r,x-t[k].recy-t[t[k].l].sz);
//如果x大于的左子树的大小+k的重复次数,
//那就在右子树中找
else return t[k].val;
//否则就是找到解了(包含了重复数字中)
}
void pred(int k,int x){//找前缀
if (k==0) return ;
if (t[k].val<x){
ans=k;pred(t[k].r,x);
//找到了更优的解,就替换之
//而且在其右子树中不可能再有更优的了
//故向其左子树中找
}else pred(t[k].l,x);
//否则就往右子树中找
}
void succ(int k,int x){//找后缀
if (k==0) return ;
if (t[k].val>x){
ans=k;succ(t[k].l,x);
}else succ(t[k].r,x);
}
int main(){
int f,x;
scanf("%d",&m);
for (int i=1;i<=m;++i){
scanf("%d%d",&f,&x);ans=0;
if (f==1) insert(root,x);
if (f==2) del(root,x);
if (f==3) printf("%d\n",atrank(root,x));
if (f==4) printf("%d\n",rerank(root,x));
if (f==5) {pred(root,x);printf("%d\n",t[ans].val);}
if (f==6) {succ(root,x);printf("%d\n",t[ans].val);}
}
return 0;
}
http://blog.csdn.net/lemonoil/article/details/71816386
01字典树:
求数组上一段连续数据的异或和最大是多少
int ch[32*MAX][2];
LL val[32*MAX];
int sz;
void init(){
mem(ch[0],0);
sz=1;
}
void inser(LL a){
int u=0;
for(int i=32;i>=0;i--){
int c=((a>>i)&1);
if(!ch[u][c]){
mem(ch[sz],0);
val[sz]=0;
ch[u][c]=sz++;
}
u=ch[u][c];
}
val[u]=a;
}
LL query(LL a){
int u=0;
for(int i=32;i>=0;i--){
int c=((a>>i)&1);
if(ch[u][c^1]) u=ch[u][c^1];
else u=ch[u][c];
}
return val[u];
}