zoukankan      html  css  js  c++  java
  • 12.28模拟赛

    T1:

    本题其实不算太简单

    xor前缀差分,转化为[l,r]中选择两个数xor最大(边界的l-1再考虑一下)

    按位贪心显然,区间取trie可持久化即可

    考虑到一个区间的取法,是从左边答案,右边答案,和左右各拿一个。

    线段树可能区间合并长度过长,复杂度无法保证

    分块?复杂度有保证!

    n,q范围也比较有趣

    mx[i][j]记录块i到块j的答案,可以O(nsqrt(n))暴力处理

    然后查询的时候边角处理一下即可。

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=12000+5;
    const int blo=115;
    struct trie{
        int ch[2];
        int sz;
    }t[N*32+N+N];
    int rt[N];
    int tot;
    int n,m;
    int a[N],b[N];//b is presum
    int mx[blo][blo];
    int L[N],R[N];
    int be[N];
    void ins(int x,int y,int c){
        rt[x]=++tot;
        t[rt[x]].sz=t[rt[y]].sz+1;
        x=rt[x],y=rt[y];
        for(reg i=30;i>=0;--i){
            ++tot;
            if(c&(1<<i)){
                t[x].ch[1]=tot;
                t[x].ch[0]=t[y].ch[0];
                x=t[x].ch[1];y=t[y].ch[1];
                t[x].sz=t[y].sz+1;
            }else{
                t[x].ch[0]=tot;
                t[x].ch[1]=t[y].ch[1];
                x=t[x].ch[0];y=t[y].ch[0];
                t[x].sz=t[y].sz+1;
            }
        }
    }
    int query(int x,int y,int c){
        x=rt[x-1],y=rt[y];
        int ret=0;
        for(reg i=30;i>=0;--i){
            if(c&(1<<i)){
                int siz=t[t[y].ch[0]].sz-t[t[x].ch[0]].sz;
                if(siz){
                    ret+=(1<<i);
                    x=t[x].ch[0];
                    y=t[y].ch[0];
                }else{
                    x=t[x].ch[1];
                    y=t[y].ch[1];
                }
            }else{
                int siz=t[t[y].ch[1]].sz-t[t[x].ch[1]].sz;
                if(siz){
                    ret+=(1<<i);
                    x=t[x].ch[1];
                    y=t[y].ch[1];
                }else{
                    x=t[x].ch[0];
                    y=t[y].ch[0];
                }
            }
        }
        return ret;
    }
    int main(){
        rd(n);rd(m);
        for(reg i=1;i<=n;++i){
            rd(a[i]);b[i]=b[i-1]^a[i];
            
            be[i]=(i-1)/blo+1;
            if(be[i]!=be[i-1]) L[be[i]]=i;
            R[be[i]]=max(R[be[i]],i);
        }
        int cnt=be[n];
        for(reg i=1;i<=n;++i){
            ins(i,i-1,b[i]);
        }//cout<<" -------------tot "<<tot<<" "<<cnt<<endl;
        
        for(reg i=1;i<=cnt;++i){
            for(reg j=L[i]+1;j<=R[i];++j){
                mx[i][i]=max(mx[i][i],query(L[i],j-1,b[j]));
            }
        }
        for(reg i=1;i<=cnt;++i){
            for(reg j=i+1;j<=cnt;++j){
                mx[i][j]=max(mx[i][j-1],mx[j][j]);
                for(reg k=L[j];k<=R[j];++k){
                    mx[i][j]=max(mx[i][j],query(L[i],k-1,b[k]));
                }
            }
        }
        int las=0;
        int l,r,x,y;
        
        while(m--){
            rd(x);rd(y);
            x%=n;y%=n;
            l=min((x+las)%n+1,(y+las)%n+1);
            r=max((x+las)%n+1,(y+las)%n+1);
            //cout<<" l r "<<l<<" "<<r<<endl;
            int ans=0;
            if(be[l]==be[r]){
                for(reg i=l+1;i<=r;++i){
                    ans=max(ans,query(l,i-1,b[i]));
                }
            }else{
                ans=max(ans,mx[be[l]+1][be[r]-1]);
                for(reg i=l;i<=R[be[l]];++i){
                    ans=max(ans,query(l+1,r,b[i]));
                }
                for(reg i=L[be[r]];i<=r;++i){
                    ans=max(ans,query(l,i-1,b[i]));
                }
            }
            ans=max(ans,query(l,r,b[l-1]));
            printf("%d
    ",ans);
            las=ans%n;
        }
        return 0;
    }
    
    }
    signed main(){
    //    freopen("xor.in","r",stdin);
    //    freopen("xor.out","w",stdout);
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/12/28 8:09:56
    */
    View Code

    本题不错!

    看似一个AC自动机,,,

    诶,字符集太大?暴力跳fail直接扫帚图卡飞

    考虑一般的AC自动机怎么做?

    有儿子的找fail[x]即可

    没有儿子进行ch[x][i]=ch[fail[x]][i]路径压缩!

    这个路径压缩保证了第一步找儿子的复杂度是O(1)的

    或者说,这里把fail的边继承了下来!

    什么东西可以路径压缩,继承之前的信息呢?

    可持久化!!

    更新完儿子的fail之后,对自己在fail[x]根的基础上,建造自己的线段树即可。

    注意:

    先把rt[x]变成rt[fail[x]]直接继承,然后再建造

    否则trie的叶子没有儿子,就没有根了。。。

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define mid ((l+r)>>1)
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=200000+5;
    const int M=200000;
    struct node{
        int nxt,to,v;
    }e[2*N];
    int hd[N],tot;
    void add(int x,int y,int z){
        e[++tot].nxt=hd[x];
        e[tot].v=z;
        e[tot].to=y;
        hd[x]=tot;
    }
    int fa[N];
    int fail[N];
    int n;
    queue<int>q;
    struct po{
        int id;
        int ls,rs;
    }t[N*20];
    int rt[N];
    int cnt;
    void upda(int &x,int y,int l,int r,int c,int d){
        if(!x) x=++cnt;
        if(l==r){
            t[x].id=d;return;
        }
        if(c<=mid) {
            t[x].rs=t[y].rs;
            upda(t[x].ls,t[y].ls,l,mid,c,d);
        }else{
            t[x].ls=t[y].ls;
            upda(t[x].rs,t[y].rs,mid+1,r,c,d);
        }
    }
    int query(int x,int l,int r,int c){
        if(!x) return 1;
        if(l==r) return t[x].id;
        if(c<=mid) return query(t[x].ls,l,mid,c);
        return query(t[x].rs,mid+1,r,c);
    }
    void AC(){
        
        for(reg i=hd[1];i;i=e[i].nxt){
            int y=e[i].to;
            int tmp=rt[1];rt[1]=0;
            upda(rt[1],tmp,1,M,e[i].v,y);
            //cout<<" st "<<y<<endl;
            fail[y]=1;q.push(y);
        }
        while(!q.empty()){
            int x=q.front();q.pop();
            //cout<<" xx "<<x<<" "<<fail[x]<<endl;
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                //cout<<" to "<<y<<" "<<e[i].v<<endl;
                fail[y]=query(rt[fail[x]],1,M,e[i].v);
                q.push(y);
            }
            int tmp=rt[fail[x]];
            rt[x]=rt[fail[x]];
            for(reg i=hd[x];i;i=e[i].nxt){
                int y=e[i].to;
                rt[x]=0;
                upda(rt[x],tmp,1,M,e[i].v,y);
                tmp=rt[x];
            }
        }
    }
    int main(){
        rd(n);
        for(reg i=2;i<=n+1;++i){
            rd(fa[i]);
            ++fa[i];
        }int x;
        for(reg i=2;i<=n+1;++i){
            rd(x);
            add(fa[i],i,x);
        }
        
        AC();
        for(reg i=2;i<=n+1;++i){
            //if(fail[i]==1) fail[i]=0;
            if(fail[i]==0) fail[i]=1;
            printf("%d ",fail[i]-1);
        }
        return 0;
    }
    
    }
    signed main(){
    //    freopen("data.in","r",stdin);
    //    freopen("std.out","w",stdout);
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/12/28 9:22:51
    */
    
    
    
        
    View Code

    本题利用了可持久化的继承和路径压缩的特点,可以算是可持久化的另外一个用途!

    (可持久化还可以建线段树省空间,以及查询历史版本)

    T3:

    本题其实并不难

    但是看到博弈论不熟悉就吓到了。。。然后考试也没有留足时间

    博弈论的操作是一个树形结构。

    决策必须是一些字符串的前缀?trie树就是博弈树!

    必然要处理先手必胜还是先手必败,trie的叶子为0,dp一遍即可。

    但是为了眼光长远,必须该输就输,所以考虑能否想输就输(这个时候目标刚好相反)

    这个时候,对方一定会想让你赢。

    所以本质相同,trie的叶子为1,再dp一遍就可以知道一个位置能否输掉了。

    然后讨论

    1.必胜,可以输掉:先手无敌,可以直接狂输,然后赢即可。

    2.必胜,不能想输就输,即先手赢且只能赢。K为奇数,先手必胜,偶数,先手必败

    3.必输,先手必输

    这样一定考虑了所有情况。

    #include<bits/stdc++.h>
    #define reg register int
    #define il inline
    #define numb (ch^'0')
    using namespace std;
    typedef long long ll;
    il void rd(int &x){
        char ch;x=0;bool fl=false;
        while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
        for(x=numb;isdigit(ch=getchar());x=x*10+numb);
        (fl==true)&&(x=-x);
    }
    namespace Miracle{
    const int N=1e5+5;
    int ch[N][26];
    char s[N];
    int t;
    int cnt;
    void ins(char *s,int l){
        int now=1;
        for(reg i=1;i<=l;++i){
            int x=s[i]-'a';
            if(ch[now][x]==0) ch[now][x]=++cnt;
            now=ch[now][x];
        }
    }
    int dfs1(int x){
        int ret=0;
        for(reg i=0;i<26;++i){
            if(ch[x][i]){
                int tmp=dfs1(ch[x][i]);
                if(tmp==0) ret=1;
            }
        }
        return ret;
    }
    int dfs2(int x){
        int ret=0;
        int has=0;
        for(reg i=0;i<26;++i){
            if(ch[x][i]) {
                ++has;
                int tmp=dfs2(ch[x][i]);
                if(tmp==0) ret=1;
            }
        }
        if(has) return ret;
        return 1;
    }
    void clear(){
        cnt=1;
        memset(ch,0,sizeof ch);
    }
    int main(){
        rd(t);
        while(t--){
            clear();
            int n;
            int k;
            rd(n);rd(k);
            for(reg i=1;i<=n;++i){
                scanf("%s",s+1);
                int len=strlen(s+1);
                ins(s,len);
            }
            int bs=dfs1(1);
            int kb=dfs2(1);
            if(bs){
                if(kb) puts("Pure");
                else{
                    if(k&1) puts("Pure");
                    else puts("Dirty");
                }
            }else{
                puts("Dirty");
            }
        }
        return 0;
    }
    
    }
    signed main(){
        Miracle::main();
        return 0;
    }
    
    /*
       Author: *Miracle*
       Date: 2018/12/28 15:11:22
    */
    View Code

    还可以简单粗暴一些:
    连续K轮,每轮换一个先手,相当于叶子作为下一层的根节点

    即:

    跑两遍dp之后,

    可以倍增处理

    也可以发现其实叶子颜色相同,会0/1重复,(本质和第一个方法就一致了)

    讨论即可。

    根据本题,博弈论的一个总结是:
    考虑必胜必败,后继状态,从边界开始处理

    还可以考虑可以输,不能输另外两种关系。

  • 相关阅读:
    希望走过的路成为未来的基石
    第三次个人作业--用例图设计
    第二次结对作业
    第一次结对作业
    第二次个人编程作业
    第一次个人编程作业(更新至2020.02.07)
    Springboot vue 前后分离 跨域 Activiti6 工作流 集成代码生成器 shiro权限
    springcloud 项目源码 微服务 分布式 Activiti6 工作流 vue.js html 跨域 前后分离
    spring cloud springboot 框架源码 activiti工作流 前后分离 集成代码生成器
    java代码生成器 快速开发平台 二次开发 外包项目利器 springmvc SSM后台框架源码
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10191886.html
Copyright © 2011-2022 走看看