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 }
来源:https://www.cnblogs.com/youhavepeople/p/7240955.html