• [机房练习赛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 }
  • 相关阅读:
    Java 注解
    java 编码设计细节
    快捷键大全
    java 常用方法
    用SelectSingleNode()方法查找xml节点一直返回null
    未能加载文件或程序集“microsoft.Build.Engine, Version=3.5.0.0,...”或它的摸一个依赖项。
    引用dll出现了黄色感叹号
    保持控件和容器之间的相对位置
    DataGridView中EnditCommit()调用之后,单元格的内容被全选了,每次输入都要鼠标点击定位到最后才能继续输入
    访问DataGridView的Rows报了OutOfIndexRangeException错误
  • 原文地址:https://www.cnblogs.com/youhavepeople/p/7240955.html
Copyright © 2020-2023  润新知