[机房练习赛7.26] YYR字符串

无尽的矩阵(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 ;
}

异或(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值