[机房练习赛7.26] YYR字符串
1 无尽的矩阵(matrix.c/cpp/pas)
1.1 题目描述
从前有一个的小矩阵,矩阵的每个元素是一个字母(区分大小写),突然有一天它发生了变异,覆盖了整个二维空间,即不停自我复制产生相同的矩阵然后无隙放置。现在二维空间已经被它占领了,但你只被告知了大小为R*C空间的内容(可能包含不完整的原矩阵),为了将它恢复原状,你需要找到满足条件的面积最小的原矩阵。
奇怪的是,同时有 T 个二维空间发生了变异,你需要尽快解决这些变异。
1.2 输入格式
第一行为一个整数T,表示二维空间数目。
接下来T组数据。每组数据第一行包含两个数 R,C,表示你被告知的空间大小;接下来 R 行,每行包含 C 个字母,表示你被告知的空间内容。
1.3 输出格式
对于每一组数据输出一行,每行只包含一个数,表示最小的原矩阵面积。
1.4 样例输入
2
2 5
ABABA
ABABA
2 8
ABCDEFAB
AAAABAAA
1.5 样例输出
2
12
1.6 数据范围与约定
对于前20%的数据R<=20,C<=20;
对于前40%的数据R<=400,C<=100;
对于100%的数据R<=5000 ,C<=100,T<=50。
将每一行hash 为一个数,对得到的新数组直接跑KMP 求最小循环节长度,列
同理。将两次求得的最小循环节长度相乘即为答案。这就是std 做法。
满分
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std; 5 char s[5005][105]; 6 int line,row,T,nxt[5005],r,c; 7 void get1( int x ){ 8 nxt[0] = -1; 9 int i = 0, j = -1; 10 while( i < c ){ 11 if( s[x][i] == s[x][j] || j == -1 ){ 12 i++; j++; 13 nxt[i] = j; 14 } 15 else j = nxt[j]; 16 } 17 } 18 void get2( int x ){ 19 nxt[0] = -1; 20 int i = 0, j = -1; 21 while( i < r ){ 22 if( s[i][x] == s[j][x] || j == -1 ){ 23 i++; j++; 24 nxt[i] = j; 25 } 26 else j = nxt[j]; 27 } 28 } 29 int main(){ 30 freopen("matrix.in","r",stdin); 31 freopen("matrix.out","w",stdout); 32 scanf("%d", &T); 33 while( T-- ){ 34 line = row = 0; 35 scanf("%d%d", &r, &c); 36 for( int i = 0; i < r; i++ ){ 37 scanf("%s", s[i]); 38 get1(i); 39 line = max(line,c-nxt[c]); 40 } 41 for( int i = 0; i < c; i++ ){ 42 get2(i); 43 row = max(row,r-nxt[r]); 44 } 45 printf("%d\n",row*line); 46 } 47 }
std:
#include<iostream> #include<ctime> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int R , C ; char A[10005][105] ; unsigned long long base=100007 , h_r[10005] , h_c[10005] , zj[10005] ; int len , fail[10005] ; char get_c(){ char c; while((c=(char)getchar())!=EOF) if(!isspace(c)) break ; return c; } int get_it(){ memset(fail,0,sizeof(fail)) ; for(int i=2;i<=len;++i){ int t=fail[i-1] ; while(t && zj[t+1]!=zj[i]) t=fail[t] ; if(zj[t+1]==zj[i]) fail[i]=t+1 ; } return len-fail[len] ; } void solve(){ scanf("%d%d",&R,&C); memset(h_r,0,sizeof(h_r)) ; memset(h_c,0,sizeof(h_c)) ; for(int i=1;i<=R;++i) for(int j=1;j<=C;++j) A[i][j]=get_c() , h_r[i]=h_r[i]*base+A[i][j] ; for(int j=1;j<=C;++j) for(int i=1;i<=R;++i) h_c[j]=h_c[j]*base+A[i][j] ; for(int i=1;i<=R;++i) zj[i]=h_r[i] ; len=R ; int ans=get_it() ; for(int i=1;i<=C;++i) zj[i]=h_c[i] ; len=C ; ans*=get_it() ; cout << ans << '\n' ; } int main(){ freopen("matrix.in","r",stdin) ; freopen("matrix.out","w",stdout) ; int T; scanf("%d",&T) ; for(int i=1;i<=T;++i) solve() ; //fprintf(stderr,"std: %d\n",clock()) ; return 0 ; }
2 异或(xor.c/cpp/pas)
2.1 题目描述
给出 n 个数,Q次询问,每次问[l,r]中最大连续异或和。
为了体现在线操作,对于每次询问(x,y):
l=min( ((x+lastans) mod n)+1 , ((y+lastans) mod n)+1 )
r=max( ((x+lastans) mod n)+1 , ((y+lastans) mod n)+1 )
2.2 输入格式
第一行为两个整数n,m,分别表示数的个数和询问次数。
接下来一行 n个数,再接下来 m行,每行两个数 x,y,表示给出询问(x,y),通过上述操作得到l和r,查询[l,r]中最大连续异或和。
2.3 输出格式
输出m行,每行一个整数表示该次询问的答案。
2.4 样例输入
3 3
1 4 3
0 1
0 1
4 3
2.5 样例输出
5
7
7
2.6 数据范围与约定
对于30%的数据,n<=500,Q<=500。
对于100%的数据,n<=12000 , Q<=6000 , 给出的数均在signed longint 范围内。
同bzoj1741
将 n 个数分成sqrt(n)个块。考虑用 w[i][j] 表示从第 i 个块开头元素到第 j 个元素这
个区间中,最大连续异或和。建可持久化Trie 树并且预处理出w 数组。预处理复杂度为 O(n
* sqrt(n) * 位数)。
查询[l,r]时,令 p 为 l 以右第一个块开头元素,那么首先可以直接得到 p 到 r 区间的
答案。再考虑上 [l,p-1] 区间中的元素,逐个在可持久化Trie 上贪心即可。查询总复杂度为
O(Q * sqrt(n) * 位数)。
std:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; typedef long long LL; const int maxn=12005; const int maxbit=31; int N,M,A[maxn]; int tr[maxn]; struct PerTrie { int next[10000005][2],num[10000005]; int id; void init(){ id=next[0][0]=next[0][1]=num[0]=0; } int f(int x,int i){ return (x>>i)&1; } void Insert(int& rt,int pre,int x,int pos) //插入 { rt=++id; next[rt][0]=next[pre][0]; next[rt][1]=next[pre][1]; num[rt]=num[pre]+1; if(pos==-1) return; int d=f(x,pos); Insert(next[rt][d],next[pre][d],x,pos-1); } int MaxXor(int l,int r,int x) //查询最大异或值,因为A[i]保存 { //的是前缀异或值,所以得到的结果就是某一段区间的异或值 int ret=0; for(int i=maxbit;i>=0;i--) { int d=f(x,i); int a=next[l][d^1],b=next[r][d^1]; if(num[b]-num[a]>0) ret|=(1<<i),l=a,r=b; else l=next[l][d],r=next[r][d]; } return ret; } }PT; int block,num,bel[maxn],dp[120][maxn]; //dp保存第几块到第几个数的区间最大异或值 void init() { tr[0]=0; PT.init(); for(int i=1;i<=N;i++) PT.Insert(tr[i],tr[i-1],A[i],maxbit); //插入 block=(int)sqrt(N+0.5); num=N/block; if(N%block) num++; //加1 memset(dp,0,sizeof(dp)); bel[0]=0; for(int i=1;i<=N;i++) bel[i]=(i-1)/block+1; //记录下属于哪个块 for(int i=1;i<=num;i++) { int st=(i-1)*block+1; for(int j=st;j<=N;j++) { dp[i][j]=max(dp[i][j-1],A[j]^A[st-1]); //可能是[st,j]这段区间 dp[i][j]=max(dp[i][j],PT.MaxXor(tr[st-1],tr[j],A[j])); //再找最大的 } } } int GetAns(int l,int r) { l--; int s=bel[l],ret=0; if(bel[r]>s) ret=dp[s+1][r]; //查询从后面一个块开始的 for(int i=l;i<=min(r,s*block);i++) { ret=max(ret,PT.MaxXor(tr[l-1],tr[r],A[i])); } return ret; } int main() { freopen("xor.in","r",stdin) ; freopen("xor.out","w",stdout) ; scanf("%d%d",&N,&M); A[0]=0; int x; for(int i=1;i<=N;i++) { scanf("%d",&x); A[i]=A[i-1]^x; } init(); int last=0,l,r; while(M--) { scanf("%d%d",&l,&r); l=(l+(LL)last)%N+1; r=(r+(LL)last)%N+1; if(l>r) swap(l,r); //printf("%d %d\n",l,r); last=GetAns(l,r); printf("%d\n",last); } return 0; }
满分
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> using namespace std; #define LL long long #define N 12005 #define T 200 int n,m,block,t,sz,root,l,r,x,y,ans; int a[N],s[N],f[T][N],g[T][N],L[N],R[N],ls[N*35],rs[N*35]; void insert( int &k, int x, int dep ){ if( !k ) k = ++sz; if( dep == -1 ) return; int d = x>>dep&1; if( d == 0 ) insert(ls[k],x,dep-1); else insert(rs[k],x,dep-1); } void query(int k,int x,int dep){ if( !k ) return; if( dep == -1 ) return; int d = x>>dep&1; if( d == 0 ){ if( rs[k] ) ans |= 1<<dep, query(rs[k],x,dep-1); else query(ls[k],x,dep-1); }if( d == 1 ){ if( ls[k] ) ans |= 1<<dep, query(ls[k],x,dep-1); else query(rs[k],x,dep-1); } } void ask(int l,int r){ int numl = (l-1)/block+1, numr = (r-1)/block+1; if( numl == numr ){ for( int i = l-1; i <= r; i++ ) for( int j = i+1; j <= r; j++ ) ans = max(ans,s[i]^s[j]); return; } for( int i = l-1; i <= R[numl]; i++ ) for( int j = L[numr]; j <= r; j++ ) ans = max( ans, s[i]^s[j] ); for( int i = numl+1; i <= numr; i++ ) ans = max(ans,f[i][r]); for( int i = numr-1; i >= numl; i-- ) ans = max(ans,g[i][l]); return; } int main(){ freopen("xor.in","r",stdin); freopen("xor.out","w",stdout); scanf("%d%d", &n, &m); for( int i = 1; i <= n; i++ ) scanf("%d", &a[i]), s[i] = s[i-1]^a[i]; s[n+1] = s[n]; block = sqrt(n*5); t = (n-1)/block+1; L[1] = 1; R[1] = block; for( int i = 2; i <= t; i++ ) L[i] = L[i-1]+block, R[i] = R[i-1]+block; R[t]=n; for( int i = 1; i <= t; i++ ){ sz = root = 0; memset(ls,0,sizeof(ls)); memset(rs,0,sizeof(rs)); for( int j = L[i]; j <= n; j++ ){ insert( root, s[n]^s[j-1], 30 ); ans=0; query( root, s[n]^s[j], 30 ); f[i][j] = max(ans,f[i][j-1]); } } for( int i = t; i; i-- ){ sz = root = 0; memset(ls,0,sizeof(ls)); memset(rs,0,sizeof(rs)); for( int j = R[i]; j; j-- ){ insert( root, s[j], 30 ); ans = 0; query( root, s[j-1], 30 ); g[i][j] = max(ans,g[i][j+1]); } } ans = 0; for( int i = 1; i <= m; i++ ){ scanf("%d%d", &l, &r); l = ((ll)l+(ll)ans)%n+1; r = ((ll)r+(ll)ans)%n+1; if(l>r) swap(l,r); ans = 0; ask(l,r); printf("%d\n",ans); } return 0; }
3魔法串(magic.c/cpp/pas)
3.1 题目描述
给你一棵n+1个结点的有根树,结点从0到n标号,其中0为根结点。
这是一棵魔法树。这棵树的每条边有一个魔力值,同一个结点连向不同子结点的边的魔力值不同。一个结点所代表的魔法串是从根一直走到这个结点,经过的魔力值依次排列形成的有序序列,另外,一个串是魔法串当且仅当它被一个结点所代表。
现在,为了使用强大的魔法,你需要对每个魔法串,找到最长的是它后缀的魔法串。为了方便输出,你只需要输出代表这个魔法串的结点的标号即可。若没有这个魔法串,请输出0。
3.2 输入格式
第一行一个整数n,代表除根以外的结点个数。
第二行 n个整数,第i个整数P_i代表标号为i的结点的父亲标号。
第三行 n个整数,第i个整数C_i代表标号为i的结点连向父亲的边的魔力值。
3.3 输出格式
输出一行n个整数,第i个整数表示第i个结点代表的魔法串的答案。
3.4 样例输入
7
0 0 1 1 2 4 5
1 2 3 2 1 1 3
3.5 样例输出
0 0 02 1 5 3
3.6 数据范围与约定
对于30%的数据,保证1<=n<=2000。
对于100%的数据,保证1<=n<=200000,0<=P_i<i,1<=C_i<=n。
用AC自动机直接跳会TLE,MLE,用主席树维护
考虑补全AC 自动机(Trie 图),考虑一个结点u 所连出的转移边与fail[u]所
连出的转移边的关系,只有u 直接连出的边会影响这些转移边,而边数是n-1 条。于是我
们考虑将fail[u]的转移边全部复制给u,再在此基础上对u 的转移边进行修改。这个如何实
现?用可持久化线段树维护每个结点的转移边即可。
1 #include<algorithm> 2 #include<cstring> 3 #include<cstdio> 4 using namespace std ; 5 const int N = 200000 + 5; 6 struct Edge{ int to,v,next; }e[N*2]; 7 int last[N],cnt,fail[N],q[N],p[N],c[N],n,head,tail,root[N],ls[N*30],rs[N*30],v[N*30],sz; 8 void insert( int u, int v, int w ){ 9 e[++cnt].to = v; e[cnt].v = w; e[cnt].next= last[u]; last[u] = cnt; 10 } 11 void modify( int &k, int l, int r, int p, int val ){ 12 ls[++sz] = ls[k]; rs[sz] = rs[k]; v[sz] = v[k]; k = sz; 13 if( l == r ){ v[k] = val; return; } 14 int mid = (l+r)>>1; 15 p<=mid ? modify( ls[k], l, mid, p, val ) : modify( rs[k], mid+1, r, p, val ); 16 } 17 int query( int k, int l, int r, int p ){ 18 if( l == r ) return v[k]; 19 int mid = (l+r)>>1; 20 return p<=mid ? query( ls[k], l, mid, p ) : query( rs[k], mid+1, r, p ); 21 } 22 int main(){ 23 freopen("magic.in","r",stdin); 24 freopen("magic.out","w",stdout); 25 scanf("%d", &n); 26 for( int i = 1; i <= n; i++ ) scanf("%d", &p[i]); 27 for( int i = 1; i <= n; i++ ) scanf("%d", &c[i]), insert(p[i],i,c[i]); 28 q[0] = 0; tail = 1; 29 while( head != tail ){ 30 int now = q[head++]; 31 root[now] = root[fail[now]]; 32 for( int i = last[now]; i; i = e[i].next ) 33 fail[q[tail++]=e[i].to] = query(root[fail[now]],1,n,e[i].v), modify(root[now],1,n,e[i].v,e[i].to); 34 } 35 for( int i = 1; i <= n; i++ ) printf("%d ", fail[i] ); 36 return 0; 37 }