zoukankan      html  css  js  c++  java
  • 20190805 NOIP模拟测试13 「矩阵游戏 · 跳房子 · 优美序列」

    考砸了  92分 rank29/63

    只T1,T3打了两个暴力,一个40,一个52,成功地滚回了第二机房,没啥好说的,题挺棒的,我太菜了

    A. 矩阵游戏

      不是水题,

      思路简单但是一下子想不出来,

      hang[i]表示i行应该要乘几,lie[j]表示j列要乘几

      对于每个点的贡献,是点的值乘上hang[i]再乘lie[j],点的值是(i-1)*m+j

      那么 $ ans=sumlimits_{i=1}^{n} hang[i]*sumlimits_{j=1}^{m}lie[j] * ((i-1)*m+j) $

      化简 $ans=sumlimits_{i=1}^{n}hang[i]*(i-1)*m * sumlimits_{j=1}^m lie[j] + sumlimits_{j=1}^mlie[j]*j*sumlimits_{i=1}^n $

      O(n)求出$ sumhang=sumlimits_{i=1}^nhang[i],sumlie=sumlimits_{j=1}^mlie[j] $

      最终$ ans=sumlimits_{i=1}^{n}hang[i]*(i-1)*m *sumlie+ sumlimits_{j=1}^mlie[j]*j*sumhang $

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int n,m,K;
    const int mod=1e9+7;
    long long hang[1100000],lie[1100000];
    int main(){
        scanf("%d%d%d",&n,&m,&K);
        for(int i=1;i<=max(n,m);i++) hang[i]=lie[i]=1;
        char opt[5];
        long long x,y;
        for(int i=1;i<=K;i++){
            scanf("%s%lld%lld",opt,&x,&y);
            if(opt[0]=='R') hang[x]=hang[x]*y%mod;
            else lie[x]=lie[x]*y%mod;
        }
        long long sumh=0,suml=0;
        for(int i=1;i<=n;i++) sumh=(sumh+hang[i])%mod;
        for(int i=1;i<=m;i++) suml=(suml+lie[i])%mod;
        long long ans=0;
        for(int i=1;i<=n;i++) ans=(ans+hang[i]*(i-1)%mod*m%mod*suml%mod)%mod;
        for(int i=1;i<=m;i++) ans=(ans+lie[i]*i%mod*sumh%mod)%mod;
        printf("%lld
    ",ans);
    }
    View Code

    B.跳房子

       大模拟,jump[i]表示第1列第i行的点蹦到第m列再蹦到第1列的位置,这个操作跳了m步,可以想到这样一直跳啊跳,会出环,而且一直在第1列徘徊

      求出在环里走一圈的所需步数设为num,注意求的时候每一跳是m步,num为环内点数*m

      设当前所在位置 px,py,一开始px=py=1;

      首先考虑move操作,可以分成五部分:
      第一部分 : 从(px,py)暴力走到第一列的某一行,这是jump的开始(如果就在第一列就不用走了)

      第二部分: 从第一列的某一行,一直跳,跳进环里(如果就在环里,还是不用跳)

      ps:此时的剩余步数要减去前两部分所走的步数(第一部分,一步一步;第二部分,一跳m步)

      第三部分: 用剩余步数C %=num, 表示不断沿环飞,每次都又回到了原地(有点傻逼~)

      第四部分 : 剩余步数(C<num)不够飞一圈了,就改成跳,每次消耗m步

      第五部分:剩余步数(C<m) 不够跳一大步了,一步步爬吧,直到爬到ans

      然后考虑change操作,每次改一个点的值,设这个点为(x,y),那么它所影响的只有三个点:左上(x-1,y-1),左(x,y-1),左下(x+1,y-1)

      对于这三个点需要修改的情况只有两种:1.原来向(x,y)走,现在不能走了 2. 原来不能走(x,y),现在要走

      先设当前修改的点(x,y)向上一格的点xup1,两格xup2,向下一格xdown1,向下两格xdown2,处理出这五个点的向右走最终到达第一列的哪一个位置

      然后对左上,左,左下三个点分别进行判断: 看现在应该向xup1,xup2,x,xdown1,xdown2哪一个点走(注意是原来没有从这儿走,现在要走),然后从这三个点分别进入update函数

      为了讲清楚,此处将三个点编号a1,a2,a3,将三个点从那五个点里选择而最终走到的一个第一列的点编号to1,to2,to3

      使劲往回搜,看第一列的哪些点会走到这儿,为了降低时间,我们向左上深搜,向左下深搜,找到第一列会走到这个点左上角和左上角,将这一区间的jump赋成to

      举个锤子,从点a1找左上角,左下角,然后把第一列从上角到下角的所有的jump改成to1

      那么为什么左上角到左下角之间都赋成to是正确的呢?看图:

      

      如果1->2是对的,那么3不可能指向4,因为如果3指向4,那么代表4的权值大于点2,那么1应该指向4,所以不可能交叉

      那么再想一下,如果我找到了一个左上,左下,那么从左上一定能走到a1,左下一定能走到a1,这就围成了一个“三角形”,里边的任何一个点都不可能越过“三角形”的边,而a1是最右边的顶点,全部的点又都是向右跳且不会跳出”三角形“,最后必然会经过点a1,所以左上左下区间内的所有的jump改成to1

      如此庞大而复杂的修改操作完成了,Lockey把代码稍稍简化了一下,删去了一些没用的东西,刚好200行

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    using namespace std;
    #define Re register
    int n,m,Q;
    int a[2100][2100],v[2100],st[2100],inst[2100],is_c[2100],jump[2100],num,la;
    int px=0,py=0;
    char opt[10];
    inline int read(){
        register int ret;
        register char r;
        while(r=getchar(),r<'0'||r>'9');ret=r-48;
        while(r=getchar(),r>='0'&&r<='9')ret=ret*10+r-48;
        return ret;
    }
    inline int dfs1(int x,int y){
        int xup=(x-1+n)%n,xdown=(x+1+n)%n;
        if(y==m-1) return a[x][0]>a[xup][0]?(a[x][0]>a[xdown][0]?x:xdown):(a[xup][0]>a[xdown][0]?xup:xdown);
        return a[x][y+1]>a[xup][y+1]?(a[x][y+1]>a[xdown][y+1]?dfs1(x,y+1):dfs1(xdown,y+1)):(a[xup][y+1]>a[xdown][y+1]?dfs1(xup,y+1):dfs1(xdown,y+1));
    }
    inline void walk(int &c,int opt){
        if(opt==1){
                while(py!=0&&c){
                    c--;
                    py=(py+1)%m;
                    int xup=(px-1+n)%n,xdown=(px+1+n)%n;
                    if(a[px][py]>a[xup][py]&&a[px][py]>a[xdown][py]) px=px;
                    else if(a[xup][py]>a[px][py]&&a[xup][py]>a[xdown][py]) px=xup;
                    else if(a[xdown][py]>a[px][py]&&a[xdown][py]>a[xup][py]) px=xdown;    
                }
                while(!is_c[px]&&c>=m){
                    c-=m;
                    px=jump[px];
                }
        }
        else{
            while(c>=m){
                c-=m;
                px=jump[px];
            }
            while(c){
                c--;
                py=(py+1)%m;
                int xup=(px-1+n)%n,xdown=(px+1+n)%n;
                if(a[px][py]>a[xup][py]&&a[px][py]>a[xdown][py]) px=px;
                else if(a[xup][py]>a[px][py]&&a[xup][py]>a[xdown][py]) px=xup;
                else if(a[xdown][py]>a[px][py]&&a[xdown][py]>a[xup][py]) px=xdown;
            }
        }
    }
    inline void dfs2(int x){
        v[x]=inst[x]=1;
        st[++st[0]]=x;
        if(inst[jump[x]]){
            int y;
            num=0;
            do{
                y=st[st[0]--];
                inst[y]=0;
                is_c[y]=1;
                num+=m;
            }while(y!=jump[x]);
        }
        else if(!v[jump[x]]) dfs2(jump[x]);
        inst[x]=0;
    }
    int update1(int x,int y){
        if(y==0) return x;
        int xu1=(x-1+n)%n,xd1=(x+1+n)%n;
        int xu2=(xu1-1+n)%n,xd2=(xd1+1+n)%n;
        int l=-1;
        if(a[x][y]>a[xu1][y]&&a[x][y]>a[xu2][y]) l=update1(xu1,y-1);
        if(l!=-1) return l;
        if(a[x][y]>a[xu1][y]&&a[x][y]>a[xd1][y]) l=update1(x,y-1);
        if(l!=-1) return l;
        if(a[x][y]>a[xd1][y]&&a[x][y]>a[xd2][y]) l=update1(xd1,y-1);
        return l;
    }
    int update2(int x,int y){
        if(y==0) return x;
        int xu1=(x-1+n)%n,xd1=(x+1+n)%n;
        int xu2=(xu1-1+n)%n,xd2=(xd1+1+n)%n;
        int r=-1;
        if(a[x][y]>a[xd1][y]&&a[x][y]>a[xd2][y]) r=update2(xd1,y-1);
        if(r!=-1) return r;
        if(a[x][y]>a[xu1][y]&&a[x][y]>a[xd1][y]) r=update2(x,y-1);
        if(r!=-1) return r;
        if(a[x][y]>a[xu1][y]&&a[x][y]>a[xu2][y]) r=update2(xu1,y-1);
        return r;
    }
    inline void update(int x,int y,int to){
        if(y==0){
            jump[x]=to;
            return;    
        }
        int xu1=(x-1+n)%n,xd1=(x+1+n)%n;
        int xu2=(xu1-1+n)%n,xd2=(xd1+1+n)%n;
        int l=-1,r=-1;
        if(a[x][y]>a[xu1][y]&&a[x][y]>a[xu2][y]){
            l=update1(xu1,y-1);
        }
        if(a[x][y]>a[xu1][y]&&a[x][y]>a[xd1][y]){
            if(l==-1) l=update1(x,y-1);
        }
        if(a[x][y]>a[xd1][y]&&a[x][y]>a[xd2][y]){
            if(l==-1) l=update1(xd1,y-1);
        }
        if(a[x][y]>a[xd1][y]&&a[x][y]>a[xd2][y]){
            r=update2(xd1,y-1);
        }    
        if(a[x][y]>a[xu1][y]&&a[x][y]>a[xd1][y]){
            if(r==-1) r=update2(x,y-1);
        }
        if(a[x][y]>a[xu1][y]&&a[x][y]>a[xu2][y]){
           if(r==-1) r=update2(xu1,y-1);    
        }
        if(l==-1) return;
        if(l>r){
            for(int i=l;i<n;i++) jump[i]=to;
            for(int i=0;i<=r;i++) jump[i]=to;
        }
        else for(int i=l;i<=r;i++) jump[i]=to;
    } 
    inline void change(int x,int y){
        int xu1=(x-1+n)%n,xd1=(x+1+n)%n;
        int xu2=(xu1-1+n)%n,xd2=(xd1+1+n)%n;
        int ny=(y-1+m)%m;
        int to,toup1,toup2,todown1,todown2;
        if(y!=0) to=dfs1(x,y);
        else to=x;
        if(y==0){
            toup1=xu1,toup2=xu2;
            todown1=xd1,todown2=xd2;
        }
        else{
            toup1=dfs1(xu1,y);
            toup2=dfs1(xu2,y);
            todown1=dfs1(xd1,y);
            todown2=dfs1(xd2,y);
        }
        if(a[xu1][y]>la||a[xu2][y]>la){
            if(a[x][y]>a[xu1][y]&&a[x][y]>a[xu2][y]) update(xu1,ny,to);
        }
        else if(a[xu1][y]<la&&a[xu2][y]<la){
            if(a[x][y]<a[xu1][y]||a[x][y]<a[xu2][y]){
                update(xu1,ny,a[xu1][y]>a[xu2][y]?toup1:toup2);
            }
        }
        if(a[xu1][y]>la||a[xd1][y]>la){
            if(a[x][y]>a[xu1][y]&&a[x][y]>a[xd1][y]) update(x,ny,to);
        }
        else if(a[xu1][y]<la&&a[xd1][y]<la){
            if(a[x][y]<a[xu1][y]||a[x][y]<a[xd1][y]){
                update(x,ny,a[xu1][y]>a[xd1][y]?toup1:todown1);
            }
        }
        if(a[xd1][y]>la||a[xd2][y]>la){
            if(a[x][y]>a[xd1][y]&&a[x][y]>a[xd2][y]) update(xd1,ny,to);
        }
        else if(a[xd1][y]<la&&a[xd2][y]<la){
            if(a[x][y]<a[xd1][y]||a[x][y]<a[xd2][y]){
                update(xd1,ny,a[xd1][y]>a[xd2][y]?todown1:todown2);
            }
        }
    }
    int main(){
        n=read(),m=read();
        for(Re int i=0;i<n;i++)
            for(Re int j=0;j<m;j++)
                a[i][j]=read();
        for(Re int i=0;i<n;i++)
            jump[i]=dfs1(i,0);
        for(Re int i=0;i<n;i++)
            if(!v[i]) dfs2(i);
        scanf("%d",&Q);
        int x,y,c;
        while(Q--){
            scanf("%s",opt);
            if(opt[0]=='m'){
                c=read();
                walk(c,1);
                c%=num;
                walk(c,0);
                printf("%d %d
    ",px+1,py+1);
            }
            else{
                x=read(),y=read(),c=read();
                x--,y--;
                la=a[x][y];
                a[x][y]=c;
                change(x,y);
                for(int i=0;i<n;i++) v[i]=is_c[i]=0;
                st[0]=0;
                for(int i=0;i<n;i++)
                    if(!v[i])
                        dfs2(i);
            }
        }
    }
    大模拟

    C. 优美序列

      ST表维护查询区间[l,r]的最大值与最小值,以及值域区间[l,r]的最左最右位置

      查询时由区间[l,r]查询值域[ll,rr],在由值域区间[ll,rr]查询区间更新[l,r],反复操作,知道r-l==rr-ll时,l,r即答案

      降低时间复杂度可以用分块或线段树,我是用的分块,有一个优化操作(当然就是分块优化的那个操作),对于在[l,r]区间内部的块的最短优美区间如果包含[l,r],那么这个优美区间就是[l,r]的最短优美区间,证明如下:

      优美区间定义是区间内所有数排列后是连续的,那么假设[l,r]内部的一个块找到的最短优美区间为[s,t]且s<=l,t>=r, 那么[s,t]内部的数排列后是连续的,如果有[l,r]的优美区间[ss,tt]比[s,t]更短(更优),那么[ss,tt]一定包含[l,r]中的那一个块,因此这个块的最短优美区间就应该是[ss,tt]而不是[s,t],所以这个块的最短优美区间且包含[l,r]区间的区间[s,t]就是[l,r]的最短优美区间直接跳出循环,输出答案即可

    #include<bits/stdc++.h>
    using namespace std;
    const int N=110000;
    int n,m,l,r,ll,rr,t,piece_num,piece_len,
    a[N],len[20],Log[N],
    maxn1[20][N],minn1[20][N],maxn2[20][N],minn2[20][N],//maxn1,minn1,记录区间[ij]中的值域最大和最小,maxn2,minn2记录值域[ij]中位置最右和最左
    mpx[2100][2100],mpy[2100][2100],belong[N];//记录块i与块j之间的区间能达到的最短优美区间的左右端点,记录i点属于哪个块
    inline void read(register int &ret){
        register char r=getchar();
        for(ret=0;r<48||r>57;r=getchar());
        while(r>=48&&r<=57)ret=(ret<<1)+(ret<<3)+(r^48),r=getchar();
        return;
    }
    int main(){
        read(n);
        piece_len=pow(n,0.666),piece_num=n/piece_len;
        for(register int i=1;i<=n;i++)
            read(a[i]),
            belong[i]=(i-1)/piece_len+1,
            maxn1[0][i]=minn1[0][i]=a[i],
            maxn2[0][a[i]]=minn2[0][a[i]]=i;
        Log[0]=-1,len[0]=1;
        for(register int i=1;i<=n;i++) Log[i]=Log[i>>1]+1;
        for(register int i=1;i<20;i++) len[i]=len[i-1]<<1;
        for(register int i=1;len[i]<=n;i++){
            for(register int j=1;j+len[i]-1<=n;j++){
                maxn1[i][j]=max(maxn1[i-1][j],maxn1[i-1][j+len[i-1]]);
                maxn2[i][j]=max(maxn2[i-1][j],maxn2[i-1][j+len[i-1]]);
                minn1[i][j]=min(minn1[i-1][j],minn1[i-1][j+len[i-1]]);
                minn2[i][j]=min(minn2[i-1][j],minn2[i-1][j+len[i-1]]);
            }
        }
        for(register int i=1;i<=piece_num;i++){
            l=(i-1)*piece_len+1,r=i*piece_len;
            t=Log[r-l+1];
            ll=min(minn1[t][l],minn1[t][r-len[t]+1]);
            rr=max(maxn1[t][l],maxn1[t][r-len[t]+1]);
            while(rr-ll!=r-l){
                t=Log[r-l+1];
                ll=min(minn1[t][l],minn1[t][r-len[t]+1]);
                      rr=max(maxn1[t][l],maxn1[t][r-len[t]+1]);    
                t=Log[rr-ll+1];
                l=min(minn2[t][ll],minn2[t][rr-len[t]+1]);
                r=max(maxn2[t][ll],maxn2[t][rr-len[t]+1]);                
            }
            mpx[i][i]=l,mpy[i][i]=r;
        }
        for(register int i=1;i<=piece_num;i++)
            for(register int j=i+1;j<=piece_num;j++)
                mpx[i][j]=min(mpx[i][j-1],mpx[j][j]),mpy[i][j]=max(mpy[i][j-1],mpy[j][j]);
        read(m);
        while(m--){
            read(l),read(r); 
               t=Log[r-l+1];
              ll=min(minn1[t][l],minn1[t][r-len[t]+1]);
            rr=max(maxn1[t][l],maxn1[t][r-len[t]+1]);
            while(rr-ll!=r-l){
                int ss=belong[l],tt=belong[r]-1;
                if(ss+1<=tt-1&&mpx[ss+1][tt-1]<=l&&mpy[ss+1][tt-1]>=r){
                    l=mpx[ss+1][tt-1],r=mpy[ss+1][tt-1];
                    break;
                }
                t=Log[r-l+1];
                 ll=min(minn1[t][l],minn1[t][r-len[t]+1]);
                rr=max(maxn1[t][l],maxn1[t][r-len[t]+1]);            
                t=Log[rr-ll+1];
                l=min(minn2[t][ll],minn2[t][rr-len[t]+1]);
                r=max(maxn2[t][ll],maxn2[t][rr-len[t]+1]);
            }
            printf("%d %d
    ",l,r);
        }
    }    
    View Code

      

  • 相关阅读:
    Codeforces
    (水题)Codeforces
    【拓展欧几里得】方程的解
    洛谷P3216 [HNOI2011]数学作业
    洛谷P1719 最大加权矩形
    洛谷P1369 矩形
    洛谷 P1236 算24点
    洛谷P2014 选课
    洛谷 P1573 栈的操作
    洛谷P1531 I Hate It
  • 原文地址:https://www.cnblogs.com/heoitys/p/11307234.html
Copyright © 2011-2022 走看看