【线段树哈希】「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$ 的子串”。那这个相当于是覆盖为历史版本的线段树上哈希值,可持久化一下应该就可以了。

只写了最简单的原题版本。

 

  1 #include<bits/stdc++.h>
  2 typedef unsigned int uint;
  3 const int maxn = 100035;
  4 const uint base = 2333;
  5 
  6 struct node
  7 {
  8     uint val[31];
  9     int tag,rnd;
 10 }f[maxn<<2];
 11 int n,m;
 12 char s[maxn];
 13 uint hsh[maxn][31],pwr[maxn],retx[31],rety[31];
 14 
 15 inline char nc(){
 16     static char buf[100000],*p1=buf,*p2=buf;
 17     return p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
 18 }
 19 #define getchar nc
 20 int read()
 21 {
 22     char ch = getchar();
 23     int num = 0, fl = 1;
 24     for (; !isdigit(ch); ch=getchar())
 25         if (ch=='-') fl = -1;
 26     for (; isdigit(ch); ch=getchar())
 27         num = (num<<1)+(num<<3)+ch-48;
 28     return num*fl;
 29 }
 30 uint hash(int i, int l, int r)
 31 {
 32     return hsh[r][i]-hsh[l-1][i]*pwr[r-l+1];
 33 }
 34 void build(int rt, int l, int r)
 35 {
 36     f[rt].tag = -1, f[rt].rnd = 0;
 37     for (int i=1; i<=26; i++) f[rt].val[i] = hash(i, l, r);
 38     if (l==r) return;
 39     int mid = (l+r)>>1;
 40     build(rt<<1, l, mid);
 41     build(rt<<1|1, mid+1, r);
 42 }
 43 void round(int rt)
 44 {
 45     for (int i=27; i>=2; i--)
 46         f[rt].val[i] = f[rt].val[i-1];
 47     f[rt].val[1] = f[rt].val[27];
 48 }
 49 void pushdown(int rt, int l, int mid, int r)
 50 {
 51     if (f[rt].tag!=-1){
 52         f[rt<<1].tag = f[rt].tag, f[rt<<1|1].tag = f[rt].tag+mid-l+1, f[rt].tag = -1;
 53         f[rt<<1].rnd = 0, f[rt<<1|1].rnd = 0;
 54         for (int i=1; i<=26; i++)
 55             f[rt<<1].val[i] = hash(i, f[rt<<1].tag, f[rt<<1].tag+mid-l), 
 56             f[rt<<1|1].val[i] = hash(i, f[rt<<1|1].tag, f[rt<<1|1].tag+r-mid-1);
 57     }
 58     f[rt<<1].rnd += f[rt].rnd, f[rt<<1|1].rnd += f[rt].rnd;
 59     for (int i=1; i<=f[rt].rnd; i++) round(rt<<1), round(rt<<1|1);
 60     f[rt].rnd = 0;
 61     
 62 }
 63 void query(int rt, int l, int r, int L, int R, uint *ret)
 64 {
 65     if (L <= l&&r <= R){
 66         for (int i=1; i<=26; i++) ret[i] = ret[i]*pwr[r-l+1]+f[rt].val[i];
 67     }else{
 68         int mid = (l+r)>>1;
 69         pushdown(rt, l, mid, r);
 70         if (L <= mid) query(rt<<1, l, mid, L, R, ret);
 71         if (R > mid) query(rt<<1|1, mid+1, r, L, R, ret);
 72     }
 73 }
 74 void addtag(int rt, int l, int r, int L, int R, int k)
 75 {
 76     if (L <= l&&r <= R){
 77         f[rt].tag = k+l-L, f[rt].rnd = 0;
 78         for (int i=1; i<=26; i++)
 79             f[rt].val[i] = hash(i, k+l-L, k+r-L);
 80     }else{
 81         int mid = (l+r)>>1;
 82         pushdown(rt, l, mid, r);
 83         if (L <= mid) addtag(rt<<1, l, mid, L, R, k);
 84         if (R > mid) addtag(rt<<1|1, mid+1, r, L, R, k);
 85     }
 86 }
 87 void circulation(int rt, int l, int r, int L, int R)
 88 {
 89     if (L <= l&&r <= R) ++f[rt].rnd, round(rt);
 90     else{
 91         int mid = (l+r)>>1;
 92         pushdown(rt, l, mid, r);
 93         if (L <= mid) circulation(rt<<1, l, mid, L, R);
 94         if (R > mid) circulation(rt<<1|1, mid+1, r, L, R);
 95     }
 96 }
 97 int main()
 98 {
 99     scanf("%s",s+1);
100     n = strlen(s+1), m = read(), pwr[0] = 1;
101     for (int i=1; i<=n; i++)
102     {
103         pwr[i] = pwr[i-1]*base;
104         for (int j=1; j<=26; j++) hsh[i][j] = hsh[i-1][j]*base+(j==s[i]-'a'+1);
105     }
106     build(1, 1, n);
107     for (int i=1; i<=m; i++)
108     {
109         int opt = read();
110         if (opt==1){
111             int x = read(), y = read(), k = read();
112             for (int i=1; i<=26; i++) retx[i] = rety[i] = 0;
113             query(1, 1, n, x, y, retx);
114             query(1, 1, n, k, k+y-x, rety);
115             bool legal = true;
116             for (int i=1; i<=26&&legal; i++)
117                 if (retx[i]!=rety[i]) legal = false;
118             puts(legal?"Y":"N");
119         }else if (opt==2){
120             int x = read(), y = read(), k = read();
121             addtag(1, 1, n, x, y, k);
122         }else{
123             int x = read(), y = read();
124             circulation(1, 1, n, x, y);
125         }
126     }
127     return 0;
128 }

 

 

 

 

END

 

posted @ 2019-07-14 21:36  AntiQuality  阅读(236)  评论(0编辑  收藏  举报