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=\sum\limits_{i=1}^{n} hang[i]*\sum\limits_{j=1}^{m}lie[j] * ((i-1)*m+j) $

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

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

  最终$ ans=\sum\limits_{i=1}^{n}hang[i]*(i-1)*m *sumlie+ \sum\limits_{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\n",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\n",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\n",l,r);
    }
}    
View Code

 

  

posted @ 2019-08-09 16:28  Lockey_T  阅读(235)  评论(5编辑  收藏  举报