【线段树哈希】「Balkan OI 2016」Haker
1A海星
题目大意
给你一个长度为 $n$ ,由小写字母构成的字符串 $S$ 和 $Q$ 个操作,每个操作是以下 3 种之一:
- 1 x y k :询问当前字符串从位置 $x$ 到 $y$ 的子串与从位置 $k$ 开始,长度为 $y-x+1$ 的子串是否相同。
- 2 x y k :将当前字符串从位置 $x$ 到 $y$ 的子串变成原始串从位置 $k$ 开始,长度为 $y−x+1$ 的子串。
- 3 x y :将当前字符串从位置 $x$ 到 $y$ 的子串中的所有 $a$ 变成 $b$ ,$b$ 变成 $c$ ,$\cdots $ ,$z$ 变成 $a$ 。
$n,q \le 10^5$
题目分析
初一看前两个操作用字符串哈希都可以轻松解决,比较难处理的是这个操作3循环位移。
如果一直只想着对字符按顺序哈希这一种狭隘的哈希,这个循环位移是没法处理的。那么从其他角度考虑,由于循环位移涉及到两个不同质字符的 交换,所以应该记录一些只关于一种同质字符的信息——它的出现位置。也就是说对它的出现位置哈希,从而实现循环位移,而比较相同只需要将$|\sum|$个字符的出现位置都比较一次就可以了。
对出现位置哈希还有一个好处就是可以处理 形式相同(即按最小表示法相同)的字符串比较。
这题另外一种变式就是把操作2改成:“将当前字符串从位置 $x$ 到 $y$ 的子串变成 当前串 从位置 $k$ 开始,长度为 $y−x+1$ 的子串”。那这个相当于是覆盖为历史版本的线段树上哈希值,可持久化一下应该就可以了。
只写了最简单的原题版本。
#include<bits/stdc++.h>
typedef unsigned int uint;
const int maxn = ;
const uint base = ; struct node
{
uint val[];
int tag,rnd;
}f[maxn<<];
int n,m;
char s[maxn];
uint hsh[maxn][],pwr[maxn],retx[],rety[]; inline char nc(){
static char buf[],*p1=buf,*p2=buf;
return p1==p2 && (p2=(p1=buf)+fread(buf,,,stdin),p1==p2)?EOF:*p1++;
}
#define getchar nc
int read()
{
char ch = getchar();
int num = , fl = ;
for (; !isdigit(ch); ch=getchar())
if (ch=='-') fl = -;
for (; isdigit(ch); ch=getchar())
num = (num<<)+(num<<)+ch-;
return num*fl;
}
uint hash(int i, int l, int r)
{
return hsh[r][i]-hsh[l-][i]*pwr[r-l+];
}
void build(int rt, int l, int r)
{
f[rt].tag = -, f[rt].rnd = ;
for (int i=; i<=; i++) f[rt].val[i] = hash(i, l, r);
if (l==r) return;
int mid = (l+r)>>;
build(rt<<, l, mid);
build(rt<<|, mid+, r);
}
void round(int rt)
{
for (int i=; i>=; i--)
f[rt].val[i] = f[rt].val[i-];
f[rt].val[] = f[rt].val[];
}
void pushdown(int rt, int l, int mid, int r)
{
if (f[rt].tag!=-){
f[rt<<].tag = f[rt].tag, f[rt<<|].tag = f[rt].tag+mid-l+, f[rt].tag = -;
f[rt<<].rnd = , f[rt<<|].rnd = ;
for (int i=; i<=; i++)
f[rt<<].val[i] = hash(i, f[rt<<].tag, f[rt<<].tag+mid-l),
f[rt<<|].val[i] = hash(i, f[rt<<|].tag, f[rt<<|].tag+r-mid-);
}
f[rt<<].rnd += f[rt].rnd, f[rt<<|].rnd += f[rt].rnd;
for (int i=; i<=f[rt].rnd; i++) round(rt<<), round(rt<<|);
f[rt].rnd = ; }
void query(int rt, int l, int r, int L, int R, uint *ret)
{
if (L <= l&&r <= R){
for (int i=; i<=; i++) ret[i] = ret[i]*pwr[r-l+]+f[rt].val[i];
}else{
int mid = (l+r)>>;
pushdown(rt, l, mid, r);
if (L <= mid) query(rt<<, l, mid, L, R, ret);
if (R > mid) query(rt<<|, mid+, r, L, R, ret);
}
}
void addtag(int rt, int l, int r, int L, int R, int k)
{
if (L <= l&&r <= R){
f[rt].tag = k+l-L, f[rt].rnd = ;
for (int i=; i<=; i++)
f[rt].val[i] = hash(i, k+l-L, k+r-L);
}else{
int mid = (l+r)>>;
pushdown(rt, l, mid, r);
if (L <= mid) addtag(rt<<, l, mid, L, R, k);
if (R > mid) addtag(rt<<|, mid+, r, L, R, k);
}
}
void circulation(int rt, int l, int r, int L, int R)
{
if (L <= l&&r <= R) ++f[rt].rnd, round(rt);
else{
int mid = (l+r)>>;
pushdown(rt, l, mid, r);
if (L <= mid) circulation(rt<<, l, mid, L, R);
if (R > mid) circulation(rt<<|, mid+, r, L, R);
}
}
int main()
{
scanf("%s",s+);
n = strlen(s+), m = read(), pwr[] = ;
for (int i=; i<=n; i++)
{
pwr[i] = pwr[i-]*base;
for (int j=; j<=; j++) hsh[i][j] = hsh[i-][j]*base+(j==s[i]-'a'+);
}
build(, , n);
for (int i=; i<=m; i++)
{
int opt = read();
if (opt==){
int x = read(), y = read(), k = read();
for (int i=; i<=; i++) retx[i] = rety[i] = ;
query(, , n, x, y, retx);
query(, , n, k, k+y-x, rety);
bool legal = true;
for (int i=; i<=&&legal; i++)
if (retx[i]!=rety[i]) legal = false;
puts(legal?"Y":"N");
}else if (opt==){
int x = read(), y = read(), k = read();
addtag(, , n, x, y, k);
}else{
int x = read(), y = read();
circulation(, , n, x, y);
}
}
return ;
}
END
【线段树哈希】「Balkan OI 2016」Haker的更多相关文章
- 线段树+哈希【CF580E】Kefa and Watch
线段树+哈希[CF580E]Kefa and Watch Description \(n\)个数的字符串,\(m + k\)个操作 1 l r k把\(l - r\)赋值为\(k\) 2 l r d询 ...
- 【博弈论】【SG函数】【线段树】Petrozavodsk Summer Training Camp 2016 Day 9: AtCoder Japanese Problems Selection, Thursday, September 1, 2016 Problem H. Cups and Beans
一开始有n个杯子,每个杯子里有一些豆子,两个人轮流操作,每次只能将一个豆子移动到其所在杯子之前的某个杯子里,不过可以移动到的范围只有一段区间.问你是否先手必胜. 一个杯子里的豆子全都等价的,因为sg函 ...
- 【线段树】Petrozavodsk Summer Training Camp 2016 Day 6: Warsaw U Contest, XVI Open Cup Onsite, Sunday, August 28, 2016 Problem H. Hay
有一些草,一开始高度都是0,它们的生长速率不同. 给你一些单增的日期,在这些日期要将>b的草的部分都割掉,问你每次割掉的部分有多少. 将草的生长速率从大到小排序,这样每次割掉的是一个后缀,而且不 ...
- Solution -「HEOI/TJOI 2016」「洛谷 P2824」排序
\(\mathcal{Description}\) Link. 给定排列 \(\{p_n\}\) 和 \(m\) 次局部排序操作,求操作完成后第 \(q\) 位的值. \(n,m\le10 ...
- UOJ #276「清华集训2016」汽水
为什么你们常数都这么小啊 UOJ #276 题意:在树上找一条链使得|边权平均值$ -k$|尽量小,$ n<=5e4$ $ Solution:$ 首先二分答案$ ans$,即我们需要找一条链使得 ...
- [LOJ#2743][DP]「JOI Open 2016」摩天大楼
题目传送门 DP 经典题 考虑从小到大把数加入排列内 如下图(\(A\) 已经经过排序): 我们考虑如上,在 \(i\) ( \(A_i\) )不断增大的过程中,维护上面直线 \(y=A_i\) 之下 ...
- [题解] [LOJ2743]「JOI Open 2016」摩天大楼
题目大意 将 \(N\) 个互不相同的整数 \(A_1 , A_2 , ⋯ , A_N\) 任意排列成 \(B_1 , B_2 , ⋯ , B_N\) . 要求 \(∑^{N−1}_{i=1} |B_ ...
- LOJ 2991 「THUSC 2016」补退选——trie+线段树合并或vector
题目:https://loj.ac/problem/2291 想了线段树合并的做法.就是用线段树维护 trie 的每个点在各种时间的操作. 然后线段树合并一番,线段树维护前缀最大值,就是维护最大子段和 ...
- LOJ 3066 - 「ROI 2016 Day2」快递(线段树合并+set 启发式合并)
LOJ 题面传送门 人傻常数大,需要狠命卡--/wq/wq 画个图可以发现两条路径相交无非以下两种情况(其中红色部分为两路径的重叠部分,粉色.绿色的部分分别表示两条路径): 考虑如何计算它们的贡献,对 ...
随机推荐
- Web工作方式
我们平时浏览网页的时候,会打开浏览器,输入网址后按下回车键,然后就会显示出你想要浏览的内容.在这个看似简单的用户行为背后,到底隐藏了些什么呢? 对于普通的上网过程,系统其实是这样做的:浏览器本身是一个 ...
- 10.hive安装
上传hive安装包并解压 给hive设置一个软链接 给hive配置环境变量 sudo vim /etc/profile #hive export HIVE_HOME=/opt/modules/hive ...
- [转帖]java注解与注释注解区别
https://baijiahao.baidu.com/s?id=1615942718081024481&wfr=spider&for=pc 还需要仔细看一下书的 书里面都有. jav ...
- localStorage 杂记
localStorage html5标准 Web 存储现在的主流浏览器,包括IE 8+.Chrome 4+.Firefox 3.5+.Opera 10.5+.Safari 4+.iPhone 2+.A ...
- UAV图像拼接软件编译环境配置
1,需要下载的软件: 依次安装qt-opensource.addin qt安装目录为C:\Qt\4.8.6 2,vs2010配置 *配置UAVBeta工程的属性,下图黑色字体部分与qt相关,取决于你的 ...
- 第2章:Python生态工具
1.Python内置小工具 1).1秒钟启动一个下载服务器: python -m SimpleHTTPServer python3 -m http.server 会在当前目录下启动一个文件下载服务器, ...
- 使用python的ctypes库实现内存的动态申请和释放
1.申请前内存占用情况 2.申请内存 from ctypes import * import time #在这里申请1G的内存,单位k mem = create_string_buffer(1024* ...
- Qt5.8.0编译QtMqtt库并使用该库连接有人云的例子
一 编译QtMqtt库Qt5.10才官方支持MQTT,但我用的Qt版本是5.8.0 Mingw_32BIT, 为了在Qt5.8.0上添加MQTT支持,需要自己编译源码 步骤: (1) git clon ...
- C#取绝对值函数
System.Math.Abs(float value); System.Math.Abs(decimal value); System.Math.Abs(int value); System.Mat ...
- 不同格式的YUV 和 RGB互转
YUV色彩空间: Y是亮度值,也就是说8位的灰度值即可组成一幅黑白图像,黑白电视机就是这样的. UV是色彩值,是给Y上色用的.U是Cb也就是RGB中的蓝色分量,V是Cr也就 ...