[CF580E]Kefa and Watch
题目大意:
维护一个由'0'~'9'构成的字符串,支持以下两种操作:
1.将指定区间内的所有字符修改为同一指定字符。
2.询问$x$是否为指定区间内的循环节。
思路:
建立一棵线段树,维护每个子串的哈希值。
实现细节:
1.Lazy-tag需要初始化成$-1$,因为$0$也是字符串中的元素,会混淆。
2.预处理出seed的$n$次幂及其前缀和,可以优化常数。
3.查询时需要合并两个区间的哈希值,需要考虑$mid<r$和$mid\geq r$两种情况。
其它:
这题调不出的时候打了一个暴力对拍,加过发现暴力能直接AC。
标算代码:
1 #include<cstdio> 2 #include<cctype> 3 #include<algorithm> 4 inline int getint() { 5 char ch; 6 while(!isdigit(ch=getchar())); 7 int x=ch^'0'; 8 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 9 return x; 10 } 11 inline int getdigit() { 12 char ch; 13 while(!isdigit(ch=getchar())); 14 return ch^'0'; 15 } 16 const int N=100001; 17 int n; 18 class SegmentTree { 19 #define mid ((b+e)>>1) 20 #define _left <<1 21 #define _right <<1|1 22 private: 23 int val[N<<2],tag[N<<2],pow[N],pre[N]; 24 static const int seed=131,mod=19260817; 25 void push_up(const int p,const int b,const int e) { 26 val[p]=((long long)val[p _left]*pow[e-mid]%mod+val[p _right])%mod; 27 } 28 void push_down(const int p,const int b,const int e) { 29 if(tag[p]==-1) return; 30 tag[p _left]=tag[p _right]=tag[p]; 31 val[p _left]=(long long)tag[p]*pre[mid-b]%mod; 32 val[p _right]=(long long)tag[p]*pre[e-mid-1]%mod; 33 tag[p]=-1; 34 } 35 int query(const int p,const int b,const int e,const int l,const int r) { 36 if((b==l)&&(e==r)) return val[p]; 37 push_down(p,b,e); 38 int ret=0; 39 if(l<=mid) ret=(long long)(ret+query(p _left,b,mid,l,std::min(mid,r)))*pow[std::max(r-mid,0)]%mod; 40 if(r>mid) ret=(ret+query(p _right,mid+1,e,std::max(mid+1,l),r))%mod; 41 return ret; 42 } 43 public: 44 SegmentTree() { 45 pre[0]=pow[0]=1; 46 for(int i=1;i<N;i++) { 47 pow[i]=(long long)pow[i-1]*seed%mod; 48 pre[i]=(pow[i]+pre[i-1])%mod; 49 } 50 } 51 void build(const int p,const int b,const int e) { 52 tag[p]=-1; 53 if(b==e) { 54 val[p]=getdigit(); 55 return; 56 } 57 build(p _left,b,mid); 58 build(p _right,mid+1,e); 59 push_up(p,b,e); 60 } 61 void modify(const int p,const int b,const int e,const int l,const int r,const int x) { 62 if((b==l)&&(e==r)) { 63 val[p]=x*pre[e-b]; 64 tag[p]=x; 65 return; 66 } 67 push_down(p,b,e); 68 if(l<=mid) modify(p _left,b,mid,l,std::min(mid,r),x); 69 if(r>mid) modify(p _right,mid+1,e,std::max(mid+1,l),r,x); 70 push_up(p,b,e); 71 } 72 bool check(const int l,const int r,const int x) { 73 if(x<=0||x>=r-l+1) return true; 74 return query(1,1,n,l,r-x)==query(1,1,n,l+x,r); 75 } 76 }; 77 SegmentTree t; 78 int main() { 79 n=getint(); 80 int m=getint()+getint(); 81 t.build(1,1,n); 82 while(m--) { 83 int d=getint(),l=getint(),r=getint(),c=getint(); 84 if(d==1) t.modify(1,1,n,l,r,c); 85 if(d==2) puts(t.check(l,r,c)?"YES":"NO"); 86 } 87 return 0; 88 }
暴力代码:
1 #include<cstdio> 2 #include<cctype> 3 inline int getint() { 4 char ch; 5 while(!isdigit(ch=getchar())); 6 int x=ch^'0'; 7 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 8 return x; 9 } 10 inline int getdigit() { 11 char ch; 12 while(!isdigit(ch=getchar())); 13 return ch^'0'; 14 } 15 int main() { 16 int n=getint(),m=getint()+getint(); 17 int a[n+1]; 18 for(int i=1;i<=n;i++) a[i]=getdigit(); 19 while(m--) { 20 int d=getint(),l=getint(),r=getint(),c=getint(); 21 if(d==1) { 22 for(int i=l;i<=r;i++) a[i]=c; 23 } 24 if(d==2) { 25 for(int i=1;i<=c;i++) { 26 for(int j=l-1+i+c;j<=r;j+=c) { 27 if(a[j]!=a[j-c]) { 28 puts("NO"); 29 goto Next; 30 } 31 } 32 } 33 puts("YES"); 34 } 35 Next:; 36 } 37 return 0; 38 }
数据生成器:
1 #include<ctime> 2 #include<cstdio> 3 #include<cstdlib> 4 int main() { 5 srand(time(NULL)); 6 int n=10,m=10; 7 printf("%d %d %d\n",n,m,0); 8 for(int i=1;i<=n;i++) { 9 int d=rand()%10; 10 printf("%d",d); 11 } 12 puts(""); 13 for(int i=1;i<=m;i++) { 14 int d=rand()%2+1,l=rand()%n+1,r=rand()%(n-l+1)+l; 15 int c=(d==1)?(rand()%10):(rand()%(r-l+1)); 16 printf("%d %d %d %d\n",d,l,r,c); 17 } 18 return 0; 19 }
对拍程序:
1 @echo off 2 :loop 3 data.exe >input.txt 4 SimpleOJ206.exe <input.txt >hash.txt 5 SimpleOJ206scx.exe <input.txt >scx.txt 6 fc hash.txt scx.txt 7 if errorlevel 1 pause 8 goto loop
用来手算哈希的计算器:
1 while 1: 2 import os 3 print(eval(input())) 4 os.system("pause")