zoukankan      html  css  js  c++  java
  • 【线段树哈希】「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

  • 相关阅读:
    信息学奥赛一本通(C++)在线评测系统——基础(一)C++语言——1009:带余除法
    1007:计算(a+b)×c的值
    1007:计算(a+b)×c的值
    1007:计算(a+b)×c的值
    1006:A+B问题
    1006:A+B问题
    1006:A+B问题
    C# 运算符 ?、??、?: 、?. 、 各种问号的用法和说明
    C# 运算符 ?、??、?: 、?. 、 各种问号的用法和说明
    在C#中??和?分别是什么意思?
  • 原文地址:https://www.cnblogs.com/antiquality/p/11185886.html
Copyright © 2011-2022 走看看