整理的算法模板合集: ACM模板


目录

  • c++自带的可持久化平衡树?rope大法好!
  • 1. 声明
  • 2. 支持操作
    • char类型的rope
    • int类型的rope
  • 3. 具体的细节
  • 4. “可持久化”
  • 5. 例题
    • 1. luogu P3919 【模板】可持久化线段树 1(可持久化数组)【代替可持久化线段树】
    • 2. 牛客 Shuffle Cards 【代替Splay】
    • 3. luogu P3402 可持久化并查集【代替可持久化并查集】
    • 4. luogu P6166 [IOI2012]scrivener【替代可持久化线段树】
    • 5. luogu AHOI2006文本编辑器editor【代替Splay】
  • 6. 总结
  • 7. C++官方自带可持久化平衡树rope的3000行源码

c++自带的可持久化平衡树?rope大法好!

C++官方说明

C++11及以后开始支持

可以当作可持久化平衡树使用,内部构造是一个块状链表

1. 声明

1)头文件

#include<ext/rope>

2)调用命名空间

using namespace __gnu_cxx;

3)声明使用

rope<int>Irp;rope<long long>Lrp;crope crp;//相当于定义成rope<char>,即定义为string类型rope<char>rop;rope<double>Drp;//rope<PII>Prp;不合法

2. 支持操作

push_back(x);      在末尾添加xinsert(pos,x);    在pos插入xerase(pos,x);    从pos开始删除x个replace(pos,x);   从pos开始换成xsubstr(pos,x);     提取pos开始x个at(x)/[x];         访问第x个元素test.clear();     清空元素

char类型的rope

  • insert(int pos, string &s, int n);

将字符串s的前n位插入rope的下标pos处

  • append(string &s,int pos,int n);

把字符串s中从下标pos开始的n个字符连接到rope的结尾,如没有参数n则把字符串s中下标pos后的所有字符连接到rope的结尾,如没有参数pos则把整个字符串s连接到rope的结尾

  • substr(int pos, int len);

提取rope的从下标pos开始的len个字符

  • at(int x);

访问rope的下标为x的元素

  • erase(int pos, int num);

从rope的下标pos开始删除num个字符

  • copy(int pos, int len, string &s);

从rope的下标pos开始的len个字符用字符串s代替,如果pos后的位数不够就补足

  • replace(int pos, string &x);

从rope的下标pos开始替换成字符串x,x的长度为从pos开始替换的位数,如果pos后的位数不够就补足


int n, m;
char a[N];
rope<char>r;
int main()
{for(int i = 0; i <= 10; ++ i)a[i] += i +'0';r.insert(0, a + 2, 8);for(int i = 0; i <= 10; ++ i)cout << r.at(i) << " ";puts("");for(int i = 0; i <= 10; ++ i)cout << r[i] << " ";puts("");return 0;}
2 3 4 5 6 7 8 9
2 3 4 5 6 7 8 9

int类型的rope

  • insert(int pos, int *s, int n);

将int数组(以下的s都是int数组)s的前n位插入rope的下标pos处,如没有参数n则将数组s的所有位都插入rope的下标pos处

  • append(int *s,int pos,int n);

把数组s中从下标pos开始的n个数连接到rope的结尾,如没有参数n则把数组s中下标pos后的所有数连接到rope的结尾,如没有参数pos则把整个数组s连接到rope的结尾

  • substr(int pos, int len);

提取rope的从下标pos开始的len个数

  • at(int x);

访问rope的下标为x的元素

  • erase(int pos, int num);

从rope的下标pos开始删除num个数

  • copy(int pos, int len, int *s);

从rope的下标pos开始的len个数用数组s代替,如果pos后的位数不够就补足

  • replace(int pos, int *x);

从rope的下标pos开始替换成数组x,x的长度为从pos开始替换的位数,如果pos后的位数不够就补足

rope<int>rp;
int main()
{rp.append(3);rp.append(1);rp.append(2);rp.append(1);rp = rp.substr(1, 3);//从1开始截3个数字,注意rope是从0开始的,所有的容器都是从0开始的for(int i = 0; i < rp.size(); ++ i)cout << rp[i] << " ";puts("");return 0;
}

输出:

1 2 1

3. 具体的细节

时间复杂度:O(nn)O(n\sqrt{n})O(nn​)(因为是用块状链表实现的)

空间复杂度:O(n)O(\sqrt{n})O(n​)

4. “可持久化”

定义方法

rope<char> *now[p];

如何申请更新一个新的可持久化版本:

now[0]=new rope<char>();

如何继承版本

now[cnt]=new rope<char>(*now[cnt-1]);

如何回到查询过去的版本

ans=now[cnt]->at(num);

这样我们就得到了一个可持久化线段树!

5. 例题

1. luogu P3919 【模板】可持久化线段树 1(可持久化数组)【代替可持久化线段树】


数据太大了…1e61e61e6…

由于本题的时空限制比较严格,所以只能拿94分,最后一个点MLE…一般的题目不会卡的这么严。

这道题因为有M个版本,也就是说空间复杂度为O(nm)O(n\sqrt{m})O(nm​) =1e9= 1e9=1e9,也就是最大开了1e91e91e9个intintint,一个intintint占4个字节,也就是B,1KB=1024B,1MB=1024KB,1Gb=1024MB,妈呀,我算了一下,我这个程序在最后一个点的时候用了大概3814MB,三个G的内存hhh…

//#pragma GCC optimize("Ofast")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<ext/rope>#define debug(x) cout << x << "ok" << endl
typedef long long ll;
#define file freopen("1.in", "r", stdin);freopen("1.out", "w", stdout);
const int N = 1e6 + 1, M = 1e5 + 7, INF = 0x3f3f3f3f;using namespace std;
using namespace __gnu_cxx;
//可持久化平衡树
typedef pair<int, int> PII;int n, m;
int a[N];
rope<int> *S[N];ll read()
{ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-')f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;
}int main()
{n = read(), m = read();S[0] = new rope<int>();S[0]->append(0);for(int i = 0; i < n; ++ i)a[i] = read();S[0]->insert(1, a, n);//从1开始(如果不使用这个方法而是每次输入一个放进去一个有个点会MLE...)//for(int i = 0; i <= n; ++ i)//    cout << S[0]->at(i) << endl;for(int i = 1; i <= m; ++ i){int v, k, a, b;v = read(), k = read();S[i] = new rope<int>(*S[v]);if(k == 1){a = read(), b = read();S[i]->replace(a, b);}else {a = read();printf("%d\n", S[i]->at(a));}}return 0;
}

2. 牛客 Shuffle Cards 【代替Splay】

题目传送门

  • 首先分析数据范围

1e51e51e5,加上是区间移动,一看就是一个数据结构问题,区间问题一般用Splay解决,但是我只会treap…。但是我们有rope这个神器!

  • 然后来分析题意

题目大意:有n个数,m次操作,每次选择从x开始长度为y的数放到数组的最前面。问m次操作后的序列。直接用rope暴力跑啊!

//#pragma GCC optimize("Ofast")
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<bitset>
#include<map>
#include<ext/rope>#define debug(x) cout << x << "ok" << endl
typedef long long ll;
#define file freopen("1.in", "r", stdin);freopen("1.out", "w", stdout);
const int N = 2e5 + 7, M = 1e5 + 7, INF = 0x3f3f3f3f;using namespace std;
using namespace __gnu_cxx;ll read()
{ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-')f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;
}int n, m, x, y;
rope<int>r;
int a[N];int main()
{n = read(), m = read();//for(int i = 1; i <= n; ++ i)//    r.push_back(i);//r.append(0);for(int i = 1; i <= n; ++ i)a[i] = i;r.insert(0, a + 1, n);//这样写会比上面那种每次都放到后面快一点while(m -- ){x = read(), y = read();r = r.substr(x - 1, y) + r.substr(0, x - 1) + r.substr(x + y - 1, n - x - y + 1);}for(int i = 0; i < n; ++ i){printf("%d%s", r[i], i == (n - 1) ? "\n" : " ");}return 0;
}

3. luogu P3402 可持久化并查集【代替可持久化并查集】


实际写法和可持久化线段树一样开一堆数组,直接暴力维护

注意:BZOJ 3673: 可持久化并查集可以AC,因为数据范围只有0<n,m<=2∗1040<n,m<=2*10^40<n,m<=2∗104

但是这道题只能拿24分…因为数据范围拉到了2e52e52e5…

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<ext/rope>
#define pii pair<int,int>
#define N 200005
using namespace std;
using namespace __gnu_cxx;
rope<int>*his[N];
int find(int x,int root)
{if(x==his[root]->at(x))return x;int t=find(his[root]->at(x),root);if(t!=his[root]->at(x))his[root]->replace(x,t);//不判的话会mle return his[root]->at(x);
}
int main()
{int n,m,last=0;scanf("%d%d",&n,&m);his[0]=new rope<int>;his[0]->push_back(0);for(int i=1;i<=n;i++)his[0]->push_back(i);for(int i=1;i<=m;i++){int op,a,b,u,v,k;scanf("%d",&op);if(op==1){scanf("%d%d",&a,&b);a^=last,b^=last;u=find(a,i-1);v=find(b,i-1);his[i]=new rope<int>(*his[i-1]);if(u==v)continue;his[i]->replace(v,u);}if(op==2){scanf("%d",&k);k^=last;his[i]=new rope<int>(*his[k]);}if(op==3){scanf("%d%d",&a,&b);a^=last,b^=last;u=find(a,i-1);v=find(b,i-1);his[i]=new rope<int>(*his[i-1]);printf("%d\n",u==v);last=u==v;}}
}

4. luogu P6166 [IOI2012]scrivener【替代可持久化线段树】

题目链接:https://www.luogu.com.cn/problem/P6166

题意

设计支持如下 3 种操作:
1.T x:在文章末尾打下一个小写字母 x。(type 操作)
2.U x:撤销最后的x 次修改操作。(Undo 操作)
(注意Query 操作并不算修改操作)
3.Q x:询问当前文章中第x 个字母并输出。(Query 操作)

操作数n<=100000 在线算法

超简单的直接实现

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;const int N = 5000007, M = 5000007, INF = 0x3f3f3f3f;ll read()
{ll x = 0, f = 1;char ch = getchar();while(ch < '0' || ch > '9'){if(ch == '-')f = -1;ch = getchar();}while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}return x * f;
}int cnt;
rope<char> * now[N];
int n, m;
int num;
char op, str;int main()
{scanf("%d", &n);now[0] = new rope<char>();for(int i = 1; i <= n; ++ i){cin >> op;if(op == 'T'){cin >> str;cnt ++ ;//新版本now[cnt] = new rope<char>(*now[cnt - 1]);now[cnt]->push_back(str);}if(op == 'U'){cin >> num;cnt ++ ;now[cnt] = new rope<char>(*now[cnt - num - 1]);//num个修改操作前的版本}if(op == 'P'){cin >> num;cout << now[cnt]->at(num) << endl;//cout << now[cnt][num] << endl;//不能这么写}}return 0;
}

某位大佬的另一种写法,用树状数组维护当前i是第几个修改操作,然后交上去0分…

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
const int maxn=1e5+10;
rope<char> *his[maxn];
int n;
int d[maxn];
inline int lowbit(int x){ return x&-x; }inline void updata(int x){ while(x<=n){ d[x]++; x+=lowbit(x); } }
inline int get(int x){ int res=0; while(x){ res+=d[x]; x-=lowbit(x); }return res; }inline char getC(){ char ch=getchar(); while(!isalpha(ch))ch=getchar(); return ch; }inline int getint()
{int res=0; char ch,ok=0;while(ch=getchar()){if(isdigit(ch)){ res*=10;res+=ch-'0';ok=1; }else if(ok)break;}return res;
}
void deb(rope<char> s)
{for(int i=0;i<s.length();i++)cout<<s[i];puts("");
}int main()
{//    freopen("type.in","r",stdin);
//    freopen("type.out","w",stdout);n=getint();his[0]=new rope<char>();for(int i=1;i<=n;i++){his[i]=new rope<char>(*his[i-1]);// deb(*his[i]);char opt=getC();if(opt=='T'){char x=getC();his[i]->push_back(x);updata(i);}else if(opt=='U'){updata(i);int x=getint();int l=1,r=i,mid,now=get(i);while(l<r){mid=(l+r)>>1;if(now-get(mid)>x) l=mid+1;else r=mid;}his[i]=his[l-1];}else if(opt=='Q'){int x=getint()-1;putchar(his[i]->at(x));putchar('\n');} // deb(*his[i]);}return 0;
}

5. luogu AHOI2006文本编辑器editor【代替Splay】

题意

设计数据结构支持,插入删除反转字符串

分析:由于rope的底层实现,insert,erase,get都是logn的
就是翻转不行,不是自己手写的打不了标记啊!!
怎么办?
答:同时维护一正一反两个rope……反转即交换两个子串……Orz……
区间循环位移?简单,拆成多个子串连起来就好了……
区间a变b b变c c变d …… z变a? 呃……维护26个rope?
区间和?滚蛋,那是线段树的活
区间kth?sorry,与数值有关的操作rope一概不支持……
5555 维修数列只能自己写了……

本题题解来源:https://www.cnblogs.com/vivym/p/3927949.html

#include <cstdio>
#include <ext/rope>
#include <iostream>
#include <algorithm>
using namespace std;
using namespace __gnu_cxx;
crope a,b,tmp;
char s[10];
int now,n,len,size;
char str[2000000],rstr[2000000];
int main()
{ scanf("%d",&n); while(n--){ scanf("%s",s); switch(s[0]){ case 'M':{scanf("%d",&now);break;} case 'P':{now--;break;} case 'N':{now++;break;} case 'G':{putchar(a[now]);putchar('\n');break;} case 'I':{ scanf("%d",&size); len=a.length(); for(int i=0;i<size;i++){ do{str[i]=getchar();} while(str[i]=='\n'); rstr[size-i-1]=str[i]; } rstr[size]=str[size]='\0'; a.insert(now,str); b.insert(len-now,rstr); break; }               case 'D':{ scanf("%d",&size); len=a.length(); a.erase(now,size); b.erase(len-now-size,size); break; } case 'R':{ scanf("%d",&size); len=a.length(); tmp=a.substr(now,size); a=a.substr(0,now) + b.substr(len-now-size,size) + a.substr(now+size,len-now-size); b=b.substr(0,len-now-size)+tmp+b.substr(len-now,now); break; } } } return 0;
}

另一种写法+详细题解
https://wa-derfulanswer.blog.luogu.org/P4567
AC 代码 :

#include <cstdio>
#include <ext/rope>const int N = 1<<22 + 7;
int n, k, cnt;
char now, inst[N], goal[N], bac[N];
struct readers{char now;bool state;readers operator >>(int &goal){goal = 0;state = true;while (now = getchar(), now < 48 || now > 57)state = now^45;while (48 <= now && now <= 57){goal = (goal<<1) + (goal<<3) + (now^48);now = getchar();}if (!state)goal = -goal;return *this;}
}reader;
__gnu_cxx::rope<char> a, b, tmp;int main(){reader>>n;while (n --){/*bef:*/scanf("%s", inst);
//      if (inst[0] == 10)goto bef;
//      printf("inst:"), puts(inst);if (inst[0] == 'I'/*"Insert"*/ || inst[0] == 'M'/* == "Move"*/ || inst[0] == 'D'/* == "Delete"*/ || inst[0] == 'R'/* == "Rotate"*/){reader>>k;if (inst[0] == 'M')cnt = k;else if (inst[0] == 'I'){register int length = a.size();for (register int i = 0;i < k;i ++){bac[k - i - 1] = goal[i] = getchar();}goal[k] = bac[k] = '\0';a.insert(cnt, goal);b.insert(length - cnt, bac);}else if (inst[0] == 'D'){register int length = a.size();a.erase(cnt, k);b.erase(length - cnt - k, k);}else if (inst[0] == 'R'){register int length = a.size();tmp = a.substr(cnt, k);a = a.substr(0, cnt) + b.substr(length - cnt - k, k) + a.substr(cnt + k, length - cnt - k);b = b.substr(0, length - cnt - k) + tmp + b.substr(length - cnt, cnt);}}else if (inst[0] == 'P'/* == "Prev"*/)cnt --;else if (inst[0] == 'N'/* == "Next"*/)cnt ++;else if (inst[0] == 'G'/* == "Get"*/){putchar(a[cnt]);if (a[cnt] != 10)putchar(10);}}    return 0;
}

6. 总结

还是不太好用,数据大了容易MLE什么的,几道模板题都不能直接A,都是只能拿部分分,所以能不用就不用,如果遇见数据比较小的(10410^4104 ~ 10510^5105非可持久化可以一试或者10410^4104的可持久化数据结构可以一试)需要写可持久化的数据结构或者平衡树的基本操作什么的可以用rope试一下,但还是不要报有太大的幻想hhh。

也就是说只要不是卡的特别严格的题,都可以用rope试一下,我们可以想出思路之后先用rope实现一下,看看思路对不对,可以先交上去试试hhh,实在不行也可以用来对拍!

7. C++官方自带可持久化平衡树rope的3000行源码

将近三千行的源码hhh,放到这里太多了,我就放到一个新的博客里了。还是读一读源码会好一点

博客链接:https://fanfansann.blog.csdn.net/article/details/109608491

参考

  • 【Rope大法好】【STL中丧心病狂的可持久化平衡树】

  • 谈c++ pb_ds库(一)rope大法好

c++自带的可持久化平衡树?rope大法好!(超详细解答 + 5道例题讲解,可直接替代可持久化的线段树、并查集、平衡树!)相关推荐

  1. 可持久化线段树【主席树】可持久化并查集【主席树+并查集】

    笼统的主席树原理 众所周知, 主席树是可以持久化的, 换言之你能知道你所维护信息的所有历史状态. 主席树是这样做的: 1. 首先建一颗朴素的线段树,代表初始状态 (下图黑色) , 也就是第0次操作后的 ...

  2. 转发 微博 Qzone 微信 一篇文章带你入门ZooKeeper实现原理!(超详细)

    转发 微博 Qzone 微信 一篇文章带你入门ZooKeeper实现原理!(超详细)

  3. 【Android App】实战项目之仿拼多多的直播带货(附源码和演示 超详细必看)

    需要源码请点赞关注收藏后评论区留言私信~~~ 近年来电商业态发生了不小的改变,传统的电商平台把商品分门别类,配上精美的图文说明供消费者挑选,新潮的电商平台则请来明星网红,开启直播秀向广大粉丝推销商品, ...

  4. 听说你想去大厂看妹子,带你看看腾讯产品运营岗超详细面经

    前言 金三银四跳槽季,你准备好了吗?希望各位小伙伴能够苦练技术,早日达成自己心仪的offer. 在这里给自己打个广告,需要的小伙伴请自行订阅. Spring系列面试题129道(附答案解析) Redis ...

  5. 手把手带你安装window10+ubuntu18.04双系统--超详细

    1.查看电脑配置 (1)操作系统的启动模式有两种:legacy+mbr模式和uefi+gpt.查看方法:按下Win+R组合键,在运行窗口中输入cmd,点击确定. 在弹出的窗口输入diskpart. 在 ...

  6. 手把手教你用python爬虫带你玩转日系P站,超详细教学

    一.页面分析 今天就说一些开场白了,直接进入主题. 首先,打开目标网址:P站 (未成年人禁止入内嗷(手动滑稽)) 结构和贴吧差不多呀,肯定要分两步. 第一步:通过主详情页,拿到各个子详情页的链接. 右 ...

  7. CE自带教程使用指南完整版过程(超详细)

    步骤一:下载并安装CE软件 1. 将压缩包解压后,打开Cheat Engine.exe 2. 选择>>zh_CN,点击确定. 3. 选择yes后出现了教程的步骤一界面,阅读完毕后选择下一步 ...

  8. 线段树开新坑:kuangbin带你飞

    写在最前面的废话 这里I以前的题是暑假刚刚开始的时候在家写的,然后多校一波就荒废了 9月开头回家一波,重新填坑,= =,kuangbin带你飞的pdf,这才一半题,后面还有一波,蓝瘦,慢慢写吧,不写题 ...

  9. 【Rope大法好】【STL中丧心病狂的可持久化平衡树】

    曾经我不会写平衡树--于是在STL中乱翻--学到了pb_ds库中的SXBK的斐波那契堆.支持kth的set,和--ext/rope 先发一个官方的 说明 (鸣谢maoxiaohan1999): htt ...

最新文章

  1. 全球智能驾驶公司列表
  2. KeyDown、KeyPress和KeyUp事件的区别与联系
  3. 《spring揭秘》读书笔记二
  4. VTK:图表之SideBySideGraphs
  5. VSCode拓展插件推荐(HTML、Node、Vue、React开发均适用)
  6. 寒门难出贵子,我当程序员让爸妈在老家长脸了
  7. Git下载安装(官网) 拉取代码的两种方法
  8. 栈(Stack)——后进先出(LIFO)的数据结构(Data Structures)
  9. C# - 获取工程里资源(图片、图标等)
  10. 最常用20000英语单词表_受够加班煎熬,我整理出10条职场人士最常用的透视表技巧!(下篇)...
  11. macOS Command - softwareupdate
  12. php 计算壬子,壬子日是哪一天 壬子日怎么算
  13. 【实战】如何在手机上实时接收微信小店订单提醒
  14. C++实现伪大素数生成算法(费马小定理判别法、米勒拉宾素数判定法)
  15. 怎么查看war包代码
  16. 那些跳槽做程序员的人,后悔了吗?
  17. 基于改进禁忌搜索算法求解TSP问题(Matlab代码实现)
  18. Mysql coalesce()函数认识和用法
  19. P5006 [yLOI2018] 大美江湖
  20. 专利学习笔记2:专利申请中的独立权利要求

热门文章

  1. 为什么U-Net在医学图像上表现优越?
  2. 综述 | 图像去噪方法比较
  3. 双十二自动刷淘宝能量,这个脚本你值得拥有
  4. ScrollView 嵌套 ListView 只显示第一行的源码分析
  5. Hadoop实战(6)_搭建Apache Hadoop的Eclipse开发环境
  6. Linux驱动技术(一) _内存申请
  7. 【JavaScript】AJAX教程
  8. 转 Android开发学习笔记:浅谈WebView
  9. 端口基础常识大全+常用端口对照
  10. 第四章 Lync server 2010的安装