【线段树哈希】「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 画个图可以发现两条路径相交无非以下两种情况(其中红色部分为两路径的重叠部分,粉色.绿色的部分分别表示两条路径): 考虑如何计算它们的贡献,对 ...
随机推荐
- python3.7环境下创建app、运行Django1.11版本项目报错Generator expression must be parenthesized
有些同学喜欢追求新鲜感~但追求新鲜感终归是要付出一点点代价的 在编程领域有一句至理名言:用东西不要用最新的! 就像每次苹果系统的升级都会有相当一部分用户的手机成砖一样 下面我们就介绍一个因版本升级带来 ...
- paper:Exploiting Query Reformulations for Web Search Result Diversification
一. 使用查询重构来用于网页搜索结果多样性 paper本文贡献: 1. 提出了一个概率框架,这个框架显式地建模了模糊查询的信息需求---利用子查询, 2. 分析了子查询的有效性---从由三个搜索引擎提 ...
- [loj#539][LibreOJ NOIP Round #1]旅游路线_倍增_dp
「LibreOJ NOIP Round #1」旅游路线 题目链接:https://loj.ac/problem/539 题解: 这个题就很神奇 首先大力$dp$很好想,因为可以把一维放到状态里以取消后 ...
- ES简介及特点
1.ES是什么? ES是一个高度可伸缩的开源的全文检索和分析引擎,它允许你以近实时的方式快速存储.搜索.分析大量数据,ES是基于Lucence开发,隐藏其复杂性,提供了简单易用的restful api ...
- [ZJOI2010]数字计数 题解
题面 这道题是一道数位DP的模板题: 因为窝太蒟蒻了,所以不会递推,只会记忆化搜索: 首先,咋暴力咋来: 将一个数分解成一个数组,这样以后方便调用: 数位DP的技巧:(用1~b的答案)-(1~a的答案 ...
- 后缀数组 LCP--模板题
题意: 给你S串和T串,用T串的所有前缀去匹配S串(匹配值是最长公共子串). 问你总值相加是多少. 思路: 先把两个S,T串倒过来,再拼接 S#T 合成一串,跑一下后缀数组 在排序好的rank里计算每 ...
- 安装laravel框架
方式一:Windows版本通过composer来下载安装laravel框架 一:laravel是php的一个web框架.laravel框架安装主要依赖composer工具,本经验就介绍一下怎么在win ...
- Spingboot+Mybatis+Oracle项目配置
配置过程参考: 项目创建:http://how2j.cn/k/springboot/springboot-eclipse/1640.html 集成Mybatis使用Oracle:https://www ...
- 安装多个ORACLE导致多个Oracle HOME的情况!
Oracle由于版本的不同,在注册表中产生的注册表信息也有所不同,但主要的键值信息还是一样的,例如Oracle10g比oracle9i在注册表中表现的更为“简洁”,在未知的情况下,获取Oracle10 ...
- O030、Launch 和 shut off 操作详解
参考https://www.cnblogs.com/CloudMan6/p/5460464.html 本节详细分析 instance launch 和 shut off 操作 ,以及如何在日志中快 ...