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 当初调了好久)

  • 相关阅读:
    2018 ACM 网络选拔赛 徐州赛区
    2018 ACM 网络选拔赛 焦作赛区
    2018 ACM 网络选拔赛 沈阳赛区
    poj 2289 网络流 and 二分查找
    poj 2446 二分图最大匹配
    poj 1469 二分图最大匹配
    poj 3249 拓扑排序 and 动态规划
    poj 3687 拓扑排序
    poj 2585 拓扑排序
    poj 1094 拓扑排序
  • 原文地址:https://www.cnblogs.com/Midoria7/p/13781220.html
Copyright © 2011-2022 走看看