zoukankan      html  css  js  c++  java
  • GMOJ 4417. 【HNOI2016模拟4.1】神奇的字符串 题解

    注意: 所有位置从0开始。

    不瞒你说我一开始以为是数学题

    对于这题,我们发现原字符串是存不下的,于是它肯定与字符串算法无关。数学题

    首先我们尝试维护对每一个位置为开头的答案是多少,但是我们发现直接维护不可取。于是反过来想,对每一个字符,从哪里为起点可以让这一位产生贡献(即对于一个在 (s) 中的位置 (i) ,寻找一堆 (x) 使得 (c_{x+i} ot= s_i) )。

    但是对于有贡献的起点位置集,这玩意碎了一地,显然是不好维护的。于是我们按照 ((a imes x+b)\%n) 的值排序,对于样例,排序的效果如下:(接下来配合这几个串食用更佳)

    012345678|位置
    ---------+----
    627384051|值
    101011010|原串
    ---------+----
    012345678|值
    000011111|新串
    

    假设现在有一个第一位的1,我们马上就可以看出值为 (0 ext{~}3) 都是有贡献的起点位置( 值为 (0 ext{~}3) 的字符为0)。

    对于第二位假设还是1,与这个1不一样的字符仍然是值为 (0 ext{~}3) 这几个位置,但问题在于我们要的是起点,而我是第二位,起点并不是我匹配的这一位,而是我在匹配 (c) 的时候的前一位。于是真正对我有贡献的起点就会发生变化。

    变化就是在原串中有贡献的一堆0左移一位,这样我才能在下一位拿到一个0使我产生贡献。

    那么原串左移一位对新串造成了什么影响?

    还是样例:

    graph LR 6[6] --+5--> 2[2] 2[2] --+5--> 7[7] 7[7] --+5--> 3[3] 3[3] --+5--> 8[8] 8[8] --+5--> 4[4] 4[4] --+5--> 0[0] 0[0] --+5--> 5[5] 5[5] --+5--> 1[1]

    可以注意到每一个位置到下一个位置都加了一个 (a) ,那么反过来到上一个位置就减了一个 (a) 。那么对于新串的值,也都是减一个 (a) 。由于值是连续的,减去一个 (a) 相对于向左循环移动 (a) 位,它还是一个完整的区间。

    可以看出对于每一个 (s) 中字符有贡献的起点位置值都是一段完整的区间(实际做时因为会从0截断所以可能会变成两段,不过不影响,如果是环形的话就是完整的一段)。那么对于某一个位置值,(s) 中的多少个字符以这个值为起点有贡献,就是以这个值为起点的答案。注意这里的位置并不是原串 (c) 中的位置,而是 (c) 中的。对于一个 (c) 中的位置 (x) ,它在新的排序串中的位置值为 ((a imes x+b)\%n)

    于是现在问题就很简单了,对于一个1,我们对 (0 ext{~}p-1) 左移若干位后的区间加1(位移的方法是对于第 (i) 位整个区间左移 (i imes a) 位,即左右端点减去左移值)。对于一个0,我们对 (p ext{~}n) 左移后的区间加1。对于查询,我们找到查询的位置 (x) 在排序串中的位置,看看这个位置被修改了多少(对多少个 (s) 中的字符有贡献),输出即可。

    就比如这样:

    graph LR subgraph 第一位的1 subgraph 第二位的0 左 subgraph 第三位的1 左 0[0] --- 1[0] 1[0] --- 2[0] end 2[0] --- 3[0] end end 3[0] --- 4[1] 4[1] --- 5[1] 5[1] --- 6[1] 6[1] --- 7[1] 7[1] --- 8[1] subgraph 第二位的0 右 subgraph 第三位的1 右 8[0] end end

    图真丑

    那么对于第一个位置(最左边的0),这个位置被三个字符打上了有贡献的标记,于是这个值的答案就是3。

    总之就是对每一个字符,把一个特定区间内的值打一个标记。对于一个,这个值的答案就是被标记的数量。

    Code

    然后就是记得动态开点,可以避免离散化。

    #include<cstdio>
    #define mid ((l+r)>>1)
    using namespace std;
    struct tree{
    	int l,r,v;
    }t[12000010];
    int last,n,a,b,p,q,m;
    char s[100010];
    template<typename T>void read(T &x){
    	char c=getchar();
    	for(;47>c||c>58;c=getchar());
    	for(x=0;47<c&&c<58;x=x*10+c-48,c=getchar());
    }
    void read(char &x){
    	for(x=getchar();x<33;x=getchar());
    }
    void sg(int m,int x,int y,int c,int l,int r){    //区间修改
    	if(x<=l&&r<=y){
    		t[m].v+=c;
    		return;
    	}
    	if(x<=mid){         //左半
    		if(!t[m].l){
    			t[m].l=++last;
    		}
    		sg(t[m].l,x,y,c,l,mid);
    	}
    	if(y>mid){         //右半
    		if(!t[m].r){
    			t[m].r=++last;
    		}
    		sg(t[m].r,x,y,c,mid+1,r);
    	}
    }
    int call(int m,int x,int l,int r){    //查询
    	int re=t[m].v;
    	if(x<=mid&&t[m].l){           //左半
    		re+=call(t[m].l,x,l,mid);
    	}else if(x>mid&&t[m].r){      //右半
    		re+=call(t[m].r,x,mid+1,r);
    	}
    	return(re);
    } 
    void add(int x,int y,int c){      //对某个区间进行取模,拆分
    	if((x=(x%n+n)%n)<(y=(y%n+n)%n)){
    		sg(0,x,y,c,0,n-1);
    	}else{
    		sg(0,0,y,c,0,n-1);
    		sg(0,x,n-1,c,0,n-1);
    	}
    }
    int main(){
    	read(n);read(a);read(b);read(p);read(m);
    	for(int i=0;i<m;i++){
    		read(s[i]);
    		if(s[i]==49){
    			add(-a*i,p-a*i-1,1);
    		}else{
    			add(p-a*i,n-a*i-1,1);
    		}
    	}
    	read(q);
    	for(int i=1;i<=q;i++){
    		char c;
    		int x;
    		read(c);read(x);
    		if(c=='Q'){
    			printf("%d
    ",call(0,(x*a+b)%n,0,n-1));
    		}else{
    			if(s[x]==49){
    				add(-a*x,p-a*x-1,-1);
    				add(p-a*x,n-a*x-1,1);
    			}else{
    				add(p-a*x,n-a*x-1,-1);
    				add(-a*x,p-a*x-1,1);
    			}
    			s[x]=97-s[x];
    		}
    	}
    }
    
  • 相关阅读:
    今天同事给介绍了一个LINQ的工具,LINQPad
    wordpress为不同的category添加不同的模板
    robotframework之使用cookies登陆
    robotframework的变量的使用
    robotframework之用户关键字的用法
    robot framework UI自动化之登录
    用U盘完成win10系统的安装
    robot framework 接口自动化之登录
    postman通过引入外部文件实现参数化
    postman连接mysql执行操作
  • 原文地址:https://www.cnblogs.com/groundwater/p/13503404.html
Copyright © 2011-2022 走看看