zoukankan      html  css  js  c++  java
  • [机房练习赛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
    ",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 << '
    ' ;
    }
    
    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
    ",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
    ",l,r);
            last=GetAns(l,r);
            printf("%d
    ",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
    ",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 }
  • 相关阅读:
    hlgoj 1766 Cubing
    Reverse Linked List
    String to Integer
    Bitwise AND of Numbers Range
    Best Time to Buy and Sell Stock III
    First Missing Positive
    Permutation Sequence
    Next Permutation
    Gray Code
    Number of Islands
  • 原文地址:https://www.cnblogs.com/youhavepeople/p/7240955.html
Copyright © 2011-2022 走看看