zoukankan      html  css  js  c++  java
  • 【考试反思】联赛模拟测试11

    每日挂分 喜闻乐见

    T3: 20 ( ightarrow) 0

    T1:One

    改编过的约瑟夫问题。不同的是每次出局的编号是变化的。直接暴力模拟依据美观程度 (O(n^2))(O(nlog n)) 不等(反正都是 60 分)

    那么考虑正解。我们现在知道的是最后剩下的人是谁,求最初的编号,是逆推。我们可以将所有编号 (-1),即 0 1 2 ... n-1,第 (i) 轮就是上次游戏后重新编号后,选择第 (i-1) 号人出局。

    我们只需要考虑如何从这一轮推到上一轮的编号。用 (x) 表示最后剩下的人在当前编号状态下的编号,那么递推式子就是:

    (x) +上一个状态下要出局的人的编号 ((n-i+1))) ( ext{mod}) 上一状态剩下的人的数量((i)

    如果不懂,可以正推一下,看新一轮是如何编号的。注意是逆推的,所以是上次出局的人是 (n-i+1)。这里说的枚举顺序都是从 (2) 开始到 (n) 的,如果枚举 (1)(n-1) 也类似,改改式子就行了。

    边界是 (x=0),因为最后只剩他一个人。最后记得要 (+1),因为题目的编号是 (1)(n) 的。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=3e3+10;
    int n;
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int main(){
    #ifndef LOCAL
        freopen("one.in","r",stdin);
        freopen("one.out","w",stdout);
    #endif
        int T=read();
        while(T--){
            n=read();
            int x=0;
            for(int i=2;i<=n;i++)
                x=(x+n-i+1)%i;
            printf("%d
    ",x+1);
        }
        return 0;
    }
    

    关于暴力模拟:

    好像大家都用的链表。不会吧不会吧,不会有人不用 vector 吧。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=3e3+10;
    int n;
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    vector<int> a;
    int main(){
    #ifndef LOCAL
        freopen("one.in","r",stdin);
        freopen("one.out","w",stdout);
    #endif
        int T=read();
        while(T--){
            n=read();
            a.clear();
            for(register int i=1;i<=n;i++)
            a.push_back(i);
            int j=0;
            for(register int i=1;i<=n-1;i++){
                j+=i-1;
                if(j>=a.size())j%=a.size();
                a.erase(a.begin()+j);
            }
            printf("%d
    ",a[0]);
        }
        return 0;
    }
    

    关于 vector 有多快:


    (Huge{邹队})太巨啦!

    T2:砖块

    简单模拟,怎么写都行。我的写法是记录砖块的放置方向(三种情况),两个端点的坐标。直接做就行。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1000+10;
    int n,Minx,Miny,Maxx,Maxy;
    char opt[maxn];
    int vis[maxn*2][maxn*2];
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    int stx,sty,edx,edy,dir;
    inline void Solve(char now){
        if(now=='N'){
            if(dir==1){
                sty++;edy+=n;dir=2;
                for(int i=sty;i<=edy;i++)
                    vis[stx][i]++;
            }else if(dir==2){
                sty+=n;edy++;dir=1;
                vis[stx][sty]++;
            }else if(dir==3){
                sty++;edy++;dir=3;
                for(int i=stx;i<=edx;i++)
                    vis[i][sty]++;
            }
        }else if(now=='S'){
            if(dir==1){
                sty-=n;edy--;dir=2;
                for(int i=sty;i<=edy;i++)
                    vis[stx][i]++;
            }else if(dir==2){
                sty--;edy-=n;dir=1;
                vis[stx][sty]++;
            }else if(dir==3){
                sty--;edy--;dir=3;
                for(int i=stx;i<=edx;i++)
                    vis[i][sty]++;
            }
        }else if(now=='W'){
            if(dir==1){
                stx-=n;edx--;dir=3;
                for(int i=stx;i<=edx;i++)
                    vis[i][sty]++;
            }else if(dir==2){
                stx--;edx--;dir=2;
                for(int i=sty;i<=edy;i++)
                    vis[stx][i]++;
            }else if(dir==3){
                stx--;edx-=n;dir=1;
                vis[stx][sty]++;
            }
        }else{
            if(dir==1){
                stx++;edx+=n;dir=3;
                for(int i=stx;i<=edx;i++)
                    vis[i][sty]++;
            }else if(dir==2){
                stx++;edx++;dir=2;
                for(int i=sty;i<=edy;i++)
                    vis[stx][i]++;
            }else if(dir==3){
                stx+=n;edx++;dir=1;
                vis[stx][sty]++;
            }
        }
        if(stx<Minx)Minx=stx;
        if(edx<Minx)Minx=edx;
        if(stx>Maxx)Maxx=stx;
        if(edx>Maxx)Maxx=edx;
        if(sty<Miny)Miny=sty;
        if(edy<Miny)Miny=edy;
        if(sty>Maxy)Maxy=sty;
        if(edy>Maxy)Maxy=edy;
    }
    
    inline void Init(){
        stx=sty=edx=edy=1000;
        Minx=Miny=0x3f3f3f3f;
        Maxx=Maxy=-1;
        dir=1;
        memset(vis,0,sizeof(vis));
    }
    
    int main(){
    #ifndef LOCAL
        freopen("block.in","r",stdin);
        freopen("block.out","w",stdout);
    #endif
        int T=read();
        while(T--){
            Init();
            n=read();
            scanf("%s",opt+1);
            int len=strlen(opt+1);
            vis[stx][sty]++;
            for(int i=1;i<=len;i++){
                Solve(opt[i]);
            }
            int Max=0;
            for(int i=Minx;i<=Maxx;i++)
                for(int j=Miny;j<=Maxy;j++)
                    if(vis[i][j]>Max)Max=vis[i][j];
            if(stx==edx){
                int len=edy-sty+1;
                for(int i=1;i<=len;i++)
                    printf("%d ",stx-1000);
                puts("");
                for(int i=sty;i<=edy;i++)
                    printf("%d ",i-1000);
                puts("");
            }else{
                int len=edx-stx+1;
                for(int i=stx;i<=edx;i++)
                    printf("%d ",i-1000);
                puts("");
                for(int i=1;i<=len;i++)
                    printf("%d ",sty-1000);
                puts("");
            }
            printf("%d
    ",Max);
        }
        return 0;
    }
    

    T3:数学

    高精神仙数学题,目前还不会。

    注意只维护后三位的方法显然是错误的。10000 的阶乘就是反例。正确答案是 008

    python 算阶乘的方法:

    import math
    math.factorial(10000)
    

    T4:甜圈

    吉司机线段树?Segment tree beats?不懂。

    考场写了个分块,居然比暴力还慢,震惊。考场上的思路是维护所加值和处理次数 (times)(cfrac{(times+1)times}{2}) 是否相等,似乎和正解差的有点远,导致想不出来。

    正解显然线段树,那么我们考虑如何维护。

    我们需要知道的是整个区间的所有值是否相同,因为首先显然不同值的甜甜圈不能一起做这个任务。那么我们只需要维护当前区间的最大值和最小值就行了,如果最大最小值相等显然就是所有值都相等的合法区间,直接赋值。不合法的区间,直接 return 掉。至于是否是执行了 (k) 次任务,最后一起查询即可。

    那么这样就可以获得和暴力一个分的好成绩。因为数据中存在合法和非法区间一个个交替,每次修改操作会使合法的继续合法,非法的继续非法的情况。解决方法就是若线段树当前节点的左右两个儿子中一个合法,一个不合法,那么视这个大区间是合法的。

    时间复杂度需要势能分析,不会,总之均摊 (O(nlog n)) 就完了。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn=1e6+10;
    int n,K,ans;
    
    inline int read(){
        int x=0;bool fopt=1;char ch=getchar();
        for(;!isdigit(ch);ch=getchar())if(ch=='-')fopt=0;
        for(;isdigit(ch);ch=getchar())x=(x<<3)+(x<<1)+ch-48;
        return fopt?x:-x;
    }
    
    #define lson (rt<<1)
    #define rson (rt<<1|1)
    int Min[maxn<<2],Max[maxn<<2],lazy[maxn<<2],vis[maxn<<2];
    
    inline void pushdown(int rt,int l,int r){
        if(lazy[rt]){
            Min[lson]=Min[rson]=Max[lson]=Max[rson]=lazy[rt];
            lazy[lson]=lazy[rson]=lazy[rt];
            lazy[rt]=0;        
        }
    }
    
    inline void pushup(int rt){
        if(vis[lson]&&vis[rson]){//震惊,一开始没写这种情况,居然过了
            vis[rt]=1;
        }else if(!vis[lson]&&!vis[rson]){
            Min[rt]=min(Min[lson],Min[rson]);
            Max[rt]=max(Max[lson],Max[rson]);
        }else if(vis[lson]||vis[rson]){
            //如果只写这句不写第一句,好像会把两个儿子都是非法区间的区间弄成合法的,不过数据过水过掉了
            Min[rt]=(!vis[lson])?Min[lson]:Min[rson];
            Max[rt]=(!vis[lson])?Max[lson]:Max[rson];
        }else vis[rt]=1;
    }
    
    void modify(int rt,int l,int r,int s,int t,int val){
        if(vis[rt]==1)return;
        if(Min[rt]==Max[rt]&&s<=l&&r<=t){
            if(val-1!=Min[rt])return vis[rt]=1,void();//要求任务是连续的
            Min[rt]=Max[rt]=val;
            lazy[rt]=val;
            return;
        }
        pushdown(rt,l,r);
        int mid=(l+r)>>1;
        if(s<=mid)modify(lson,l,mid,s,t,val);
        if(t>mid)modify(rson,mid+1,r,s,t,val);
        pushup(rt);
    }
    
    int query(int rt,int l,int r,int p){
        if(vis[rt]==1)return 0;
        if(l==r)return Min[rt]==K?1:0;//听说数据锅了,导致如果判<K会错
        pushdown(rt,l,r);
        int mid=(l+r)>>1;
        if(p<=mid)return query(lson,l,mid,p);
        else return query(rson,mid+1,r,p);
    }
    
    int main(){
    #ifndef LOCAL
        freopen("deco.in","r",stdin);
        freopen("deco.out","w",stdout);
    #endif
        n=read();K=read();
        int T=read();
        while(T--){
            int l=read(),r=read(),c=read();
            modify(1,1,n,l,r,c);
        }
        for(int i=1;i<=n;i++)
            ans+=query(1,1,n,i);
        printf("%d
    ",ans);
        return 0;
    }
    

    官方思路:

    将每个点都视为一个字符串,不妨对每个字符串哈希。

    如果最终哈希值等于要求的哈希值,那么合法。

    区间修改哈希值,直接用线段树维护乘法加法标记就可以了。

    但是感觉比维护最大最小值麻烦点,就没写。(毕竟线段树 2 当初调了好久)

  • 相关阅读:
    C#中异步和多线程的区别
    猫 老鼠 人的编程题
    C#中数组、ArrayList和List三者的区别
    经典.net面试题目
    sql有几种删除表数据的方式
    内存池的实现
    A*算法为什么是最优的
    传教士与野人问题
    d3d导致cairo不正常
    c++中的signal机制
  • 原文地址:https://www.cnblogs.com/Midoria7/p/13781220.html
Copyright © 2011-2022 走看看