2017.7.26 机房测试(KMP/Hash,分块+可持久化Trie树贪心,主席树+AC自动机)
2017-07-26 20:38
253 查看
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。
【解题报告】
做法1:hash求出每一行可能的循环节长度,取公共的最小循环节长度即可,列同理。将两次求得的最小循环节长度相乘即为答案。较慢,可能会超时。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<ctime> using namespace std; unsigned long long base1=1000000007 , base2=998244353 , pow1[10005] , pow2[10005] , h[10005][105] ; int R , C ; char mat[10005][105] ; char get_c(){ char c; while((c=(char)getchar())!=EOF) if(!isspace(c)) break ; return c; } unsigned long long get_h(int x1,int y1,int x2,int y2){ return h[x2][y2]-h[x1-1][y2]*pow1[x2-x1+1]-h[x2][y1-1]*pow2[y2-y1+1]+h[x1-1][y1-1]*pow1[x2-x1+1]*pow2[y2-y1+1] ; } bool check(int a,int b){ for(int i=1;i<=R;i+=a){ for(int j=1;j<=C;j+=b){ int ti=min(i+a-1,R) , tj=min(j+b-1,C) ; if(get_h(1,1,ti-i+1,tj-j+1)!=get_h(i,j,ti,tj)) return false ; } } return true ; } void solve(){ scanf("%d%d",&R,&C) ; memset(h,0,sizeof(h)) ; for(int i=1;i<=R;++i) for(int j=1;j<=C;++j) mat[i][j]=get_c() , h[i][j]=h[i-1][j]*base1+h[i][j-1]*base2-h[i-1][j-1]*base1*base2+mat[i][j] ; int ans=(1<<30)-1 ; for(int i=1;i<=R;++i){ for(int j=1;j<=C;++j){ if(i*j>=ans) continue ; if(check(i,j)) ans=i*j ; } } cout << ans << '\n' ; } int main(){ freopen("matrix.in","r",stdin) ; freopen("HASH_matrix.out","w",stdout) ; int T; pow1[0]=pow2[0]=1 ; for(int i=1;i<=10000;++i) pow1[i]=pow1[i-1]*base1 ; for(int i=1;i<=10000;++i) pow2[i]=pow2[i-1]*base2 ; scanf("%d",&T) ; while(T--) solve() ; fprintf(stderr,"%d\n",clock()) ; return 0 ; }
做法 2:将每一行hash为一个数,对得到的新数组直接跑KMP求最小循环节长度,列同理。将两次求得的最小循环节长度相乘即为答案。这就是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 ; }
做法3,水过去,可能是数据太水了
然而最后被卡掉了,(可以试一试随机找一个位置开始for或者倒着for,应该比较难卡)
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; int t,R,C,nxt[5005]; char s[5005][105], tp[5005]; int Kmp(char *s,int len) { memset(nxt,0,sizeof(nxt)); int i=0,j=-1; nxt[0]=-1; while(i<len) { if(j==-1||s[i]==s[j]) { ++i;++j; nxt[i]=j; } else j=nxt[j]; } return len-nxt[len]; } int main() { freopen( "matrix.in", "r", stdin ); freopen( "matrix.out", "w", stdout ); for(scanf("%d",&t);t;--t) { scanf("%d%d",&R,&C); for(int i=1;i<=R;++i) scanf("%s",s[i]); int ans1=0; for(int i=1;i<=R;++i) ans1=max(ans1,Kmp(s[i],C)); int ans2=0; for(int i=1;i<=C;++i) { for(int j=1;j<=R;++j) tp[j-1]=s[j][i]; ans2=max(ans2,Kmp(tp,R)); } printf("%d\n",ans1*ans2); } 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 范围内。
【解题报告】
将 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) * 位数)。
代码如下:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; #define maxn 12005 #define 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-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); 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+last)%N+1; r=(r+last)%N+1; last=GetAns(l,r); printf("%d\n",last); } return 0; }
[b]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
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 200010 #define M 6000010 struct Edge {int p,v,w;}e ; struct Node {int l,r,v;}t[M]; int f ,q ,ql,qr,rt ,n,p ,c ,hed ,e_cnt,tot; void addedge(int x,int y,int w) { e[++e_cnt].p=y; e[e_cnt].v=hed[x]; e[e_cnt].w=w; hed[x]=e_cnt; } void modify(int &x,int l,int r,int p,int v) { int y=x;x=++tot;t[x]=t[y]; if(l==r) { t[x].v=v; return; } int mid=(l+r)>>1; if(p<=mid) return modify(t[x].l,l,mid,p,v); else return modify(t[x].r,mid+1,r,p,v); } int query(int x,int l,int r,int p) { if(l==r) return t[x].v; int mid=(l+r)>>1; if(p<=mid) return query(t[x].l,l,mid,p); return query(t[x].r,mid+1,r,p); } int main() { freopen("magic.in","r",stdin); freopen("magic.out","w",stdout); scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&p[i]); for(int i=1;i<=n;++i) scanf("%d",&c[i]),addedge(p[i],i,c[i]); for(q[qr ++]=0;ql^qr;ql++) { int x=q[ql]; rt[x]=rt[f[x]]; for(int i=hed[x];i;i=e[i].v) { f[q[qr++]=e[i].p]=query(rt[f[x]],1,n,e[i].w); modify(rt[x],1,n,e[i].w,e[i].p); } } for(int i=1;i<=n;i++) printf("%d ",f[i]); return 0; }
相关文章推荐
- 机房测试 字符串 【Hash+KMP】【分块+可持久化Trie树+贪心】【AC自动机+主席树】
- BZOJ 2741 【FOTILE模拟赛】L 分块+可持久化Trie树
- 2017.7.7 机房测试 (模拟,贪心,二分)
- 7.26机房报零赛——无尽的矩阵【kmp+hash】
- 【BZOJ2741】【FOTILE模拟赛】L 分块+可持久化Trie树
- 2017.7.3 机房测试(模拟,贪心,图论)
- bzoj 2741 【FOTILE模拟赛】L 分块 可持久化trie树
- 集训DAY1 机房测试(贪心,图论)
- 字符串训练赛 By YYR AC自动机 + KMP/HASH + 贪心
- [BZOJ2741]【FOTILE模拟赛】L(分块+可持久化trie树)
- BZOJ 2741【FOTILE模拟赛】L 分块+可持久化Trie树
- bzoj2741: 【FOTILE模拟赛】L【可持久化trie树+分块】
- BZOJ 2741 详解(分块 可持久化Trie树)
- 【bzoj2741】[FOTILE模拟赛]L 可持久化Trie树+分块
- 7.26 机房欢乐赛 T1 无尽的矩阵 (hash + KMP)
- 7.26机房报零赛——无尽的矩阵【kmp+hash】
- [队内测试Day10.12]贪心+状压+分块+树状数组
- 【bzoj3261】【最大异或和】可持久化trie树+贪心
- 论可持久化Trie树贪心在区间最大最小值的运用
- 【loli的胡策】测试3.26 (贪心+hash)