• 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.10.13辗转相除法
    关于……
    友情链接
    李群笔记
    c++中的复数
    python快速傅里叶变换
    多参数函数共轭梯度法寻找极值:gsl_multimin_fdfminimizer
    python: matplotlib.pyplot 制作动图
    简易威尔逊云室计划
    大规模矩阵对角化方法:Lanczos
  • 原文地址:https://www.cnblogs.com/Miracevin/p/10191886.html
Copyright © 2020-2023  润新知