题解-洛谷P5217 贫穷
给定长度为 \(n\) 的初始文本 \(s\),有 \(m\) 个如下操作:
- \(\texttt{I x c}\),在第 \(x\) 个字母后面插入一个 \(c\)。
- \(\texttt{D x}\),删除第 \(x\) 个字母。
- \(\texttt{R x y}\),反转当前文本中的区间 \([x,y]\)。
- \(\texttt{P x}\),输出初始文本中第 \(x\) 个字母在当前文本中的位置。特别地,若不存在,输出 \(0\)。
- \(\texttt{T x}\),输出当前文本中第 \(x\) 个字母。
- \(\texttt{Q x y}\),输出当前文本中区间 \([x,y]\) 内出现过的字母的种类数。
数据范围:\(1\le n,m\le 10^5\)。
初学平衡树的蒟蒻太蒟蒻了,这题做了 \(5\) 个小时。
蒟蒻是用 \(\tt fhqTreap\) 做的,虽然对付这题 \(\tt Splay\) 更自然,但是 \(\tt fhqTreap\) 代码短。
- 维护节点信息:
const int N=1e5,T=2e5;
// N为初始文本长度,T为平衡树节点最大个数
int o[N+7];
// 记录每个初始文本字母对应的平衡树节点
int sz[T+7],fa[T+7],ls[T+7],rs[T+7],v[T+7],sm[T+7],p[T+7],mk[T+7];
// sz:节点的子树大小
// fa:节点的父亲节点,用于4操作中求rank
// ls/rs:左右儿子节点
// v:该节点对应的字母(-'a')
// sm:子树的字母总集状压(0<=sm[x]<(1<<26))
// p:fhqTreap精华随机数权值(用于维护堆)
// mk:翻转标记,用于解决3操作
- \(\tt fhqTreap\) 基本操作:
void up(int x){
if(ls[x]) fa[ls[x]]=x;
if(rs[x]) fa[rs[x]]=x;
sz[x]=sz[ls[x]]+sz[rs[x]]+1;
sm[x]=sm[ls[x]]|sm[rs[x]]|1<<v[x];
}
void down(int x){if(mk[x]) swap(ls[x],rs[x]),mk[ls[x]]^=1,mk[rs[x]]^=1,mk[x]=0;}
int wen(int x,int y=rand()){return v[++cnt]=x,p[cnt]=y,up(cnt),cnt;}
int merge(int x,int y){
if(!x||!y) return x^y;
if(p[x]<p[y]) return down(x),rs[x]=merge(rs[x],y),up(x),x;
return down(y),ls[y]=merge(x,ls[y]),up(y),y;
}
void split(int u,int k,int&x,int&y){
if(!u) return void(x=y=0);
down(u); //这东西一定要写在这里,要不然不知道ls[u]是不是真的ls[u]
if(k<=sz[ls[u]]) y=u,split(ls[y],k,x,ls[y]),fa[x]=0;
else x=u,split(rs[x],k-sz[ls[u]]-1,rs[x],y),fa[y]=0;
up(u);
}
- 题目中的操作:
\(\color{#44a897}{\texttt{[0]}}\) 插入文本:野蛮 \(\tt merge\)。
for(int i=1;i<=n;i++) rt=merge(rt,o[i]=wen(s[i]-'a'));
\(\color{#44a897}{\texttt{[1]}}\) 插入字符:套路 \(\tt split\),套路 \(\tt merge\)。
scanf("%d %s",&a,&c[1]);
split(rt,a,L,R);
rt=merge(merge(L,wen(c[1]-'a')),R);
\(\color{#44a897}{\texttt{[2]}}\) 删除字符:先把节点分裂出来,然后把两边合并。为了操作 \(4\) 可以看出一个点是否被删,在被删节点权值上做标记。
scanf("%d",&a);
split(rt,a,L,R),split(L,a-1,L,M);
v[M]=-1,rt=merge(L,R);
\(\color{#44a897}{\texttt{[3]}}\) 翻转区间:先把区间分裂出来,然后打翻转标记,最后不忘把树合回去。
scanf("%d%d",&a,&b);
split(rt,b,L,R),split(L,a-1,L,M);
mk[M]^=1,rt=merge(merge(L,M),R);
\(\color{#44a897}{\texttt{[4]}}\) 查询排名:如果节点权值有删除标记输出 \(0\)。否则先把节点到根的路径从上到下下放标记,然后从下向上求该节点前面的节点数。
void updown(int x){if(fa[x]) updown(fa[x]);down(x);}
int frank(int x){
updown(x);
int res=sz[ls[x]]+1;
for(int i=x;fa[i];i=fa[i])if(rs[fa[i]]==i) res+=sz[ls[fa[i]]]+1;
return res;
}
scanf("%d",&a);
if(v[o[a]]==-1) puts("0");
else printf("%d\n",frank(o[a]));
\(\color{#44a897}{\texttt{[5]}}\) 输出位置字母:相当于求个 \(\tt kth\),可以套路 \(\tt split\) 求。
scanf("%d",&a);
split(rt,a,L,R),split(L,a-1,L,M);
printf("%c\n",'a'+v[M]);
rt=merge(merge(L,M),R);
\(\color{#44a897}{\texttt{[6]}}\) 区间字母种类:先把区间分裂出来,答案即分裂出的根节点的子树字母集状压中的 \(1\) 的个数。
scanf("%d%d",&a,&b);
split(rt,b,L,R),split(L,a-1,L,M);
printf("%d\n",bit(sm[M])),rt=merge(merge(L,M),R);
- 调试与解释
\(\color{#efca55}{\texttt{[1]}}\) 输出当前字符串:求平衡树的中序遍历,写个 \(\tt Dfs\)。
void Print(int x){
down(x);
if(ls[x]) Print(ls[x]);
// printf("[%d<-%d->%d] (sz%d,v[%c],p%d,fa%d)\n",ls[x],x,rs[x],sz[x],v[x]+'a',p[x],fa[x]);
printf("%c",v[x]+'a');
if(rs[x]) Print(rs[x]);
}
\(\color{#efca55}{\texttt{[2]}}\) 为什么有些时候写了 \(\tt Print\) 就对了,注释掉就挂了:\(\tt Print\) 函数帮你把整棵树的翻转标记下放了,如果出现这种情况说明你的操作过程中标记下放不完全。如果要验证你的代码除了标记下放都是正确的,可以把 \(\tt Print\) 中的输出去掉,每次操作完都 \(\tt Print\),然后交一发,如果 \(\tt ac\) 两个点,\(\tt tle\) 八个点,说明你的代码除了标记下放都是正确的。
好了结束了,蒟蒻又写了一篇无意义题解,放代码吧:
#include <bits/stdc++.h>
using namespace std;
//Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair(a,b)
#define x(a) a.first
#define y(a) a.second
#define b(a) a.begin()
#define e(a) a.end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
const int inf=0x3f3f3f3f;
const ll INF=0x3f3f3f3f3f3f3f3f;
//Data
const int N=1e5,T=2e5;
int n,m,o[N+7];
char s[N+7],c[3];
int bit(int x){return x?bit(x-(x&-x))+1:0;}
//Fhqtreap
int rt,cnt,sz[T+7],fa[T+7],ls[T+7],rs[T+7],v[T+7],sm[T+7],p[T+7],mk[T+7];
void up(int x){
if(ls[x]) fa[ls[x]]=x;
if(rs[x]) fa[rs[x]]=x;
sz[x]=sz[ls[x]]+sz[rs[x]]+1;
sm[x]=sm[ls[x]]|sm[rs[x]]|1<<v[x];
}
void down(int x){if(mk[x]) swap(ls[x],rs[x]),mk[ls[x]]^=1,mk[rs[x]]^=1,mk[x]=0;}
int wen(int x,int y=rand()){return v[++cnt]=x,p[cnt]=y,up(cnt),cnt;}
int merge(int x,int y){
if(!x||!y) return x^y;
if(p[x]<p[y]) return down(x),rs[x]=merge(rs[x],y),up(x),x;
return down(y),ls[y]=merge(x,ls[y]),up(y),y;
}
void split(int u,int k,int&x,int&y){
if(!u) return void(x=y=0);
down(u);
if(k<=sz[ls[u]]) y=u,split(ls[y],k,x,ls[y]),fa[x]=0;
else x=u,split(rs[x],k-sz[ls[u]]-1,rs[x],y),fa[y]=0;
up(u);
}
void updown(int x){if(fa[x]) updown(fa[x]);down(x);}
int frank(int x){
updown(x);
int res=sz[ls[x]]+1;
for(int i=x;fa[i];i=fa[i])if(rs[fa[i]]==i) res+=sz[ls[fa[i]]]+1;
return res;
}
void Print(int x){
down(x);
if(ls[x]) Print(ls[x]);
// printf("[%d<-%d->%d] (sz%d,v[%c],p%d,fa%d)\n",ls[x],x,rs[x],sz[x],v[x]+'a',p[x],fa[x]);
printf("%c",v[x]+'a');
if(rs[x]) Print(rs[x]);
}
//Main
int main(){
scanf("%d%d\n%s",&n,&m,&s[1]);
for(int i=1;i<=n;i++) rt=merge(rt,o[i]=wen(s[i]-'a'));
// puts("now---");
// Print(rt); puts("");
// puts("++++++");
for(int i=1,a,b,L,M,R;i<=m;i++){
scanf("\n%s ",&c[1]);
if(c[1]=='I'){
scanf("%d %s",&a,&c[1]);
split(rt,a,L,R);
rt=merge(merge(L,wen(c[1]-'a')),R);
} else if(c[1]=='D'){
scanf("%d",&a);
split(rt,a,L,R),split(L,a-1,L,M);
v[M]=-1,rt=merge(L,R);
} else if(c[1]=='R'){
scanf("%d%d",&a,&b);
split(rt,b,L,R),split(L,a-1,L,M);
mk[M]^=1,rt=merge(merge(L,M),R);
} else if(c[1]=='P'){
scanf("%d",&a);
if(v[o[a]]==-1) puts("0");
else printf("%d\n",frank(o[a]));
} else if(c[1]=='T'){
scanf("%d",&a);
split(rt,a,L,R),split(L,a-1,L,M);
printf("%c\n",'a'+v[M]);
rt=merge(merge(L,M),R);
} else if(c[1]=='Q'){
scanf("%d%d",&a,&b);
split(rt,b,L,R),split(L,a-1,L,M);
printf("%d\n",bit(sm[M])),rt=merge(merge(L,M),R);
}
// puts("now---");
// Print(rt);
// puts("");
// puts("++++++");
}
return 0;
}
祝大家学习愉快!
题解-洛谷P5217 贫穷的更多相关文章
- 题解 洛谷P5018【对称二叉树】(noip2018T4)
\(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...
- 题解 洛谷 P3396 【哈希冲突】(根号分治)
根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...
- 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)
题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...
- 题解-洛谷P4229 某位歌姬的故事
题面 洛谷P4229 某位歌姬的故事 \(T\) 组测试数据.有 \(n\) 个音节,每个音节 \(h_i\in[1,A]\),还有 \(m\) 个限制 \((l_i,r_i,g_i)\) 表示 \( ...
- 题解-洛谷P4724 【模板】三维凸包
洛谷P4724 [模板]三维凸包 给出空间中 \(n\) 个点 \(p_i\),求凸包表面积. 数据范围:\(1\le n\le 2000\). 这篇题解因为是世界上最逊的人写的,所以也会有求凸包体积 ...
- 题解-洛谷P4859 已经没有什么好害怕的了
洛谷P4859 已经没有什么好害怕的了 给定 \(n\) 和 \(k\),\(n\) 个糖果能量 \(a_i\) 和 \(n\) 个药片能量 \(b_i\),每个 \(a_i\) 和 \(b_i\) ...
- 题解 洛谷 P2010 【回文日期】
By:Soroak 洛谷博客 知识点:模拟+暴力枚举 思路:题目中有提到闰年然后很多人就认为,闰年是需要判断的其实,含有2月29号的回文串,前四位是一个闰年那么我们就可以直接进行暴力枚举 一些小细节: ...
- 题解 洛谷P2158 【[SDOI2008]仪仗队】
本文搬自本人洛谷博客 题目 本文进行了一定的更新 优化了 Markdown 中 Latex 语句的运用,加强了可读性 补充了"我们仍不曾知晓得 消失的 性质5 ",加强了推导的严谨 ...
- 题解 洛谷P2959 【[USACO09OCT]悠闲漫步The Leisurely Stroll】
原题:洛谷P2959 不得不说这道题的图有点吓人,但实际上很多都没有用 通过题上说的“三岔路口”(对于每一个节点有三条连接,其中一条连接父节点,另外两条连接子节点)和数据,可以那些乱七八糟的路和牧场看 ...
随机推荐
- SSY的队列 hash+记忆化
题目描述 \(SSY\) 是班集体育委员,总喜欢把班级同学排成各种奇怪的队形,现在班级里有 \(N\) 个身高互不相同的同学,请你求出这 \(N\) 个人的所有排列中任意两个相邻同学的身高差均不为给定 ...
- ImportError: No module named 'chardet'
1.使用requsets出现这个错误,ImportError: No module named 'chardet' 原因:requests依赖其他一些模块 解决:依次使用pip安装即可 pip ins ...
- RayFire的下载与安装方法
RayFire的下载与安装方法 发布时间:2020/10/12 近几年,电影中融入了越来越多的动画元素,其中的爆炸场景更是十分吸引眼球.小编不禁好奇,什么样的插件能做出来如此好玩的特效,上网搜索一番发 ...
- Camtasia中对录制视频进行编辑——旁白
相信很多人都遇见过想要录制视频,但是不知道在电脑上用哪一款软件比较好,害怕自己录的视频导出来之后会有水印,或者在录制的过程中遇到麻烦,更或者下载一款带有病毒的软件.那么今天我便给大家推荐一款专业录制屏 ...
- Spring 事件监听机制及原理分析
简介 在JAVA体系中,有支持实现事件监听机制,在Spring 中也专门提供了一套事件机制的接口,方便我们实现.比如我们可以实现当用户注册后,给他发送一封邮件告诉他注册成功的一些信息,比如用户订阅的主 ...
- 【移动自动化】【五】常用API
常用API click: 点击 sendKeys: 输入内容 swipe: 滑动 TouchAction:这也是手势操作 github https://github.com/wangxiao9/app ...
- ssh命令的常用使用场景
目录 一.最简单的登陆 二.登陆+执行命令 三.端口转发 四.参考 一.最简单的登陆 就是简单登陆一下主机,默认端口22 ssh {hostname}@{host_ip} ➜ Charles ssh ...
- 电脑adb命令给智能电视安装APK
配置环境 1.电脑需要配置好adb系统环境 具体操作较复杂,请自行百度. 2.电视打开adb命令 在电视的关于界面通过遥控器「上上下下左右左右」进入工厂模式,在「高级设置」-「其他」中的「adb开关」 ...
- K8ssandra——专为Kubernetes云原生数据而生
DataStax最近发布了K8ssandra--一个开源的.部署于Kubernetes上的Apache Cassandra全新发行版本.K8ssandra一站式集合了在Kubernetes上部署开源版 ...
- C#(一)基础篇—类型与变量
于今日起学习巩固C#基础 2020-12-01 本随笔用于个人回忆理解,记录当天学习过程,内容多从书中整理与自我学习了解,如有问题麻烦指正 以后有时间会单独分版块叙述 不管什么语言,都从一个Hello ...