4417. 【HNOI2016模拟4.1】神奇的字符串

Description

Input

Output

Sample Input

9 5 6 4 3
101
11
Query 0
Query 1
Query 2
Query 3
Query 4
Query 5
Query 6
Query 7
Query 8
Change 1
Query 3

Sample Output

0
3
0
2
2
0
3
0
3
1

Data Constraint

Solution

首先,对于(a,n)=1,不存在两个相同的数,使得a*i=a*j ( %n ),其中i<j<n。

考虑反证法:反正它是对的,证毕。

假设存在a*i(%n)=a*j(%n),使得i<j<n,那么

(a%n)*(i%n)=(a%n)*(j%n)  (%n)

(a%n)*i=(a%n)*j   (%n)

因为(a,n)=1,所以a%n>0,等式两边同时除以(a%n),得:

i=j

与i<j矛盾,

所以不存在两个不同的i、j使得:a*i=a*j  (%n)。

同理,(a*i+b)%n,也不存在不同的i、j使得(a*i+b)与(a*j+b)相等。

设 d[ i ]=( a*i+b )%n  ,c[ i ]=[ d[ i ]>=p ]

那么我们知道每一个d[ i ]都不同,将d为第一关键字,c为第二关键字排序后,得到了d 从0~n-1的排列,同时对应的0~p-1的c[ i ]为0,p~n-1的c[ i ]为1。

首先如果s只有一位,

那么我们判断这一位是1还是0,如果是1,在所有c[ i ]为0的位置上打上加1标记,查询的时候如果查询到0的位置那么答案就是1,否则是0。

考虑如果在原先没排序的d数组中,不好打标记,而在排完序后,所有c[]为0的位置在是一段连续的区间(即下标为0~p-1)

那么建立一棵[ 0 , n-1 ]的权值线段树

对于s串中第一个位置为1,那么就在d[]中0~p-1的位置用线段树打上+1标记,如果是0,就在d[]数组中p~n-1的位置用线段树打上+1标记。表示d[]中这些c[]为0的位置与s[]串的第一位不同。

考虑s有两位。

那么将线段树的区间标记含义扩展一下,

设j为使得(a*j+b)%n=d[ i ]的 j

则:d[]数组中第i个点被打上的标记数 表示的是在c[]串中以第j个点为开头的往后的长度为m(s串的长度)的字符串与整个s[]对应位置不同的个数总和。

那么对于s串的第二个位置,我们找出所有c[]串中为1的位置,然后在这些c[]串为1位置的前一个位置(串中的位置)打上+1标记。由于d[ i-1 ]可以由( d[ i ]- a+n)%n得到,那么c[]串中的前一个位置,就相当于排完序后d[]数组的前a个位置。

同样,将串中的位置对应到d[]数组中中,如果s串的第二位为1,那么就在d[]中0~p-1整体向左移动a位得到的区间打上+1标记,如果为0,则在p~n-1整体向左移动a位得到的区间打上+1标记,这里注意区间左右端点要处理一下(有可能要分成两段区间,即[ 0,r ]和[ l ,n-1 ] )

同理,对于s[]串的第三位,就在对应区间整体向左移动2a位的区间打上+1标记。

对于s[]串的第i位,就在区间整体左移(i-1)*a位的区间打上+1标记。

那么对于修改操作,判断是s[]串的第几位,把原先的标记清除之后再打上新的标记即可。

对于查询操作,先算出读入的c[]串中的位置所对应的在线段树中的位置(即(ax+b)%n),然后直接单点查询线段树中的第(ax+b)%n个位置的标记个数即可。

Code 

#include<cstdio>
#include<algorithm>
#include<cstring>
#define I int
#define F(i,a,b) for(I i=a;i<=b;i++)
#define N 100005
using namespace std;
I n,x,a,b,p,m,q,s[N],l,r,tr[N<<6],ls[N<<6],rs[N<<6],tot;
char c;
void R(I &x){
	x=0;c=getchar();
	while(c<'0'||c>'9') c=getchar();
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
}
void insl(I x){if(!ls[x]) ls[x]=++tot;}
void insr(I x){if(!rs[x]) rs[x]=++tot;}
void down(I x){
	if(!tr[x]) return;
	insl(x),insr(x);
	tr[ls[x]]+=tr[x],tr[rs[x]]+=tr[x];
	tr[x]=0;
}
void add(I l2,I r2,I k,I x=0,I l=0,I r=n-1){
	if(l==l2&&r==r2){tr[x]+=k;return;}
	down(x);
	I M=l+r>>1;
	if(r2<=M){insl(x),add(l2,r2,k,ls[x],l,M);}
	else if(l2>M){insr(x),add(l2,r2,k,rs[x],M+1,r);}
	else{
		insl(x),add(l2,M,k,ls[x],l,M);
		insr(x),add(M+1,r2,k,rs[x],M+1,r);
	}
}
I qry(I k,I x=0,I l=0,I r=n-1){
	if(l==r) return tr[x];
	down(x);
	I M=l+r>>1;
	if(k<=M) return ls[x]?qry(k,ls[x],l,M):0;
	return rs[x]?qry(k,rs[x],M+1,r):0;
}
void work(I l,I r,I k){
	if(l<=r) add(l,r,k);
	else{add(l,n-1,k);add(0,r,k);}
}
I main(){
	freopen("string.in","r",stdin);
	freopen("string.out","w",stdout);
	R(n),R(a),R(b),R(p),R(m);
	F(i,1,m){
		c=getchar();
		while(c!='0'&&c!='1') c=getchar();
		s[i]=c-'0';
		if(s[i]) l=((n-(i-1)*a)%n+n)%n,r=((p-1-(i-1)*a)%n+n)%n;
		else l=((p-(i-1)*a)%n+n)%n,r=((n-1-(i-1)*a)%n+n)%n;
		work(l,r,1);
	}
	R(q);
	while(q--){
		c=getchar();
		while(c!='C'&&c!='Q') c=getchar();
		if(c=='Q'){
			F(i,1,4) c=getchar();
			R(x);x=(x*a+b)%n;
			printf("%d\n",qry(x));
			continue;
		}
		F(i,1,5) c=getchar();
		R(x);
		if(s[x+1]){
			work(((n-x*a)%n+n)%n,((p-1-x*a)%n+n)%n,-1);
			work(((p-x*a)%n+n)%n,((n-1-x*a)%n+n)%n,1);
		}
		else{
			work(((p-x*a)%n+n)%n,((n-1-x*a)%n+n)%n,-1);
			work(((n-x*a)%n+n)%n,((p-1-x*a)%n+n)%n,1);
		}
		s[x+1]^=1;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/zsjzliziyang/article/details/108012717
今日推荐