• 幻想乡模拟赛S1试题+题解+标程


    Problem 1 东风谷早苗(robot.cpp/c/pas)
    题目描述 在幻想乡,东风谷早苗是以高达控闻名的高中生宅巫女。某一天,早苗终于入手了最新款的钢达姆模型。作为最新的钢达姆,当然有了与以往不同的功能了,那就是它能够自动行走,厉害吧(好吧,我自重)。早苗的新模型可以按照输入的命令进行移动,命令包含’E’、’S’、’W’、’N’四种,分别对应四个不同的方向,依次为东、南、西、北。执行某个命令时,它会向着对应方向移动一个单位。作为新型机器人,自然不会只单单执行一个命令,它可以执行命令串。对于输入的命令串,每一秒它会按照命令行动一次。而执行完命令串最后一个命令后,会自动从头开始循环。在0时刻时早苗将钢达姆放置在了(0,0)的位置,并且输入了命令串。她想要知道T秒后钢达姆所在的位置坐标。
    输入格式 第1行:一个字符串,表示早苗输入的命令串,保证至少有1个命令
    第2行:一个正整数T
    输出格式 第1行:两个整数,表示T秒时,钢达姆的坐标
    输入样例 NSWWNSNEEWN
    12
    输出样例 -1 3
    数据范围 对于60%的数据:T <= 500,000且命令串长度 <= 5,000
    对于100%的数据:T <= 2,000,000,000且命令串长度<= 5,000
    注意 向东移动,坐标改变改变为(X+1,Y);
    向南移动,坐标改变改变为(X,Y-1);
    向西移动,坐标改变改变为(X-1,Y);
    向北移动,坐标改变改变为(X,Y+1);

    Robot 东风谷早苗
    题目大意 读入一串长度为N的命令字符’N’、’E’、’W’、’S’。每一秒钟按照命令移动一次,不断重复,求问T秒之后,所在位置的坐标。起始位置为(0,0),地图无穷大。
    考察算法 字符串处理 数学题
    算法一 从第0秒开始模拟每一秒的位置。
    时间复杂度:O(T) 期望得分:60
    算法二 由于命令串是重复执行的,所以我们可以先计算出一个周期后机器人的移动量。比如在第一个周期后,机器人从(0,0)移动到(△X,△Y),那么每个周期之后机器人坐标的改变为(X+△X,Y+△Y)。设T/N = A…B,那么机器人一共会运行的周期数为A,所以在A个周期后,机器人的坐标为(A*△X,A*△Y)。之后机器人还会移动B步,此时就可以直接在(A*△X,A*△Y)的基础上枚举这B步的移动。
    时间复杂度:O(N) 期望得分:100

    #include <iostream>
    using namespace std;
    #define MAXL 5009
    const int dr[4][2] = {{1, 0}, {0, -1}, {-1, 0}, {0, 1}};
    char command[ MAXL ];
    int TimeLimit;
    int main()
    {
        scanf("%s", command);
        scanf("%d", &TimeLimit);
        int cycle, rest, times, order, delta[2], last[2];
        cycle = strlen( command );
        rest = TimeLimit % cycle;
        times = TimeLimit / cycle;
        delta[0] = delta[1] = 0;
        last[0] = last[1]= 0;
        for (int i = 0; i < cycle; i++)
        {
            switch ( command[i] )
            {
                case 'E' : order = 0; break;
                case 'S' : order = 1; break;
                case 'W' : order = 2; break;
                case 'N' : order = 3; break;
                default : break;
            }
            for (int j = 0; j <= 1; j++)
            {
                delta[j] += dr[ order ][j];
                if (i < rest) last[j] += dr[ order ][j];
            }
        }
        for (int j = 0; j <= 1; j++)
            last[j] += times * delta[j];
        printf("%d %d\n", last[0], last[1]);
        return 0;
    }

    Problem 2 西行寺幽幽子(spring.cpp/c/pas)
    题目描述 在幻想乡,西行寺幽幽子是以贪吃闻名的亡灵。不过幽幽子可不是只会吃,至少她还管理着亡灵界。话说在幽幽子居住的白玉楼有一颗常年不开花的樱树——西行妖。幽幽子决定去收集人间的春度,聚集起来让西行妖开花。很快,作为幽幽子家园艺师的魂魄妖梦收集到了M个单位的春度。并且在这段时间里,幽幽子计算出要让西行妖开出一朵花需要N个单位的春度。现在幽幽子想要知道,使用所有的春度,能够让西行妖开出多少朵花。
    输入格式 第1行:一个正整数M
    第2行:一个正整数N
    N,M的位数不超过L,L的范围在题目后面给出
    输出格式 第1行:一个整数ans,表示能开出花的朵数
    输入样例 73861758
    12471
    输出样例 5922
    数据范围 对于60%的数据:L <= 2,000且ans <= 2,000
    对于100%的数据:L <= 20,000且ans <= 2,000,000,000

    Spring 西行寺幽幽子
    题目大意 高精度数N除以高精度数M,求商
    考察算法 高精度数 二分答案/二进制拆分
    算法一 从0开始枚举累加,直到当前值超过了N。此算法只需要使用加法和比较。
    对于这种算法是否使用压位高精度给分相同。
    时间复杂度:O(AnsL) 期望得分:60
    算法二 在枚举商的时候采用二分答案的方法。如果M和Mid的乘积小于或等于N,Check的值为True;否则为False。二分的伪代码如下:
    While (Left + 1 < Right)
    Begin
    Mid <- (Left + Right) / 2;
    if Check(Mid) Then Left <- Mid;
    Else Right <- Mid;
    End
    时间复杂度:O(LlogAns) 期望得分:100
    算法三 因为Ans可以转化成二进制的形式,建立高精度数组t[32],t[i] = 2^i * M。从高位向低位进行枚举,如果对于当前位k,N >=t[k],那么Ans加上2^k,N减去t[k]。其本质相当于Ans = 2^p[1 +2^p[2]+ … +2^p[j],而N = 2^p[1] *M+2^p[2]*M+…+2^p[j]*M+rest(rest < M),p数组是将Ans表示为二进制后该位为1的位置。比如5(10) = 101(2),那么5所对应的p数组为{1,3}。
    由于我们需要求的是Ans的值,所以必须从高位向低位枚举,否则会出现错误。如果有疑问,读者可以尝试从低位向高位枚举,看看输出的结果。
    时间复杂度:O(LlogAns) 期望得分:100

    #include <iostream>
    using namespace std;
    #define MAXL 20009
    typedef int hp[ MAXL ];
    char str[ MAXL ];
    hp first, second, now, t[ 32 ];
    inline int max(int x, int y)
    {
        return x > y ? x : y;
    }
    struct HP
    {
        hp box;
        void Str_to_hp(char *str, hp &a)
        {
            Set(a);
            a[0] = strlen( str );
            for (int i = 1; i <= a[0]; i++)
                a[i] = str[ a[0] - i ] - '0';
            return ;
        }
        void Set(hp &a)
        {
            memset(a, 0, sizeof(a));
            a[0] = 1;
            return ;
        }
        int Compare(hp a, hp b)
        {
            if (a[0] > b[0]) return  1;
            if (a[0] < b[0]) return -1;
            for (int i = a[0]; i >= 1; i--)
            {
                if (a[i] > b[i]) return  1;
                if (a[i] < b[i]) return -1;
            }
            return 0;
        }
        void Add(hp a, hp b, hp &c)
        {
            memset(box, 0, sizeof(box));
            box[0] = max(a[0], b[0]);
            for (int i = 1; i <= box[0]; i++)
                box[i] = a[i] + b[i];
            box[0]++;
            for (int i = 1; i <= box[0]; i++)
                if (box[i] >= 10)
                    box[i + 1]++, box[i] %= 10;
            while (!box[ box[0] ] && box[0] > 1) box[0]--;
            memcpy(c, box, sizeof(c));
            return ;
        }
        void Sub(hp a, hp b, hp &c)
        {
            memset(box, 0, sizeof(box));
            box[0] = a[0];
            for (int i = 1; i <= box[0]; i++)
                box[i] = a[i] - b[i];
            for (int i = 1; i <= box[0]; i++)
                if (box[i] < 0)
                    box[i + 1]--, box[i] += 10;
            while (!box[ box[0] ] && box[0] > 1) box[0]--;
            memcpy(c, box, sizeof(box));
            return ;
        }
    }    Op;
    int main()
    {
        int ans = 0;
        scanf("%s", str);
        Op.Str_to_hp(str, first);
        scanf("%s", str);
        Op.Str_to_hp(str, second);
        memcpy(t[1], second, sizeof(t[1]));
        for (int i = 2; i <= 31; i++)
            Op.Add(t[i - 1], t[i - 1], t[i]);
        for (int i = 31; i >= 1; i--)
            if (Op.Compare(first, t[i]) == 1)
            {
                ans |= 1 << (i - 1);
                Op.Sub(first, t[i], first);
            }
        printf("%d\n", ans);
        return 0;
    }

    Problem 3 琪露诺(iceroad.cpp/c/pas)
    题目描述 在幻想乡,琪露诺是以笨蛋闻名的冰之妖精。某一天,琪露诺又在玩速冻青蛙,就是用冰把青蛙瞬间冻起来。但是这只青蛙比以往的要聪明许多,在琪露诺来之前就已经跑到了河的对岸。于是琪露诺决定到河岸去追青蛙。小河可以看作一列格子依次编号为0到N,琪露诺只能从编号小的格子移动到编号大的格子。而且琪露诺按照一种特殊的方式进行移动,当她在格子i时,她只会移动到i+L到i+R中的一格。你问为什么她这么移动,这还不简单,因为她是笨蛋啊。每一个格子都有一个冰冻指数A[i],编号为0的格子冰冻指数为0。当琪露诺停留在那一格时就可以得到那一格的冰冻指数A[i]。琪露诺希望能够在到达对岸时,获取最大的冰冻指数,这样她才能狠狠地教训那只青蛙。但是由于她实在是太笨了,所以她决定拜托你帮它决定怎样前进。开始时,琪露诺在编号0的格子上,只要她下一步的位置编号大于N就算到达对岸。
    输入格式 第1行:3个正整数N, L, R
    第2行:N+1个整数,第i个数表示编号为i-1的格子的冰冻指数A[i-1]
    输出格式 第1行:一个整数,表示最大冰冻指数。保证不超过2^31-1
    第2行:空格分开的若干个整数,表示琪露诺前进的路线,最后输出-1表示到达对岸
    输入样例 5 2 3
    0 12 3 11 7 -2
    输出样例 11
    0 3 -1
    数据范围 对于60%的数据:N <= 10,000
    对于100%的数据:N <= 200,000
    对于所有数据 -1,000 <= A[i] <= 1,000且1 <= L <= R <= N
    注意 此题采用Special Judge

    Iceroad 琪露诺
    题目大意 存在一行格子,有各自的分值。从当前一格可以移动到前面L到R范围内的格子,并取得当前分值。求从左到右的最大得分即方案。
    考察算法 Dp 优先队列/单调队列
    算法一 从1到N行进行枚举,状态转移方程为:
    f[i] = Max{f[j]} + A[i]; (i – R <= j <= i - L)
    最后取从f[N – R + 1]到f[N]中的最大值。
    方案的输出可采用数组prev[]记录,prev[i]记录计算f[i]时所用的j的值。
    时间复杂度:O(N^2) 期望得分:60
    算法二 在算法一的基础上使用优先队列或线段树取出[i – R, i – L]中的最大值,具体实现这里就不说明了。在标程中,优先队列使用的是大根堆。
    时间复杂度:O(NlogN) 期望得分:100
    算法三
    维护一个在[i-R,i-L]内f[i]单调递减的决策队列:Q[i]记录决策位置,当队首元素位置小于i-R时队首出队,当f[i-l+1]优于队尾决策时队尾出队,然后将新的f[i-l+1]加入队尾。易知此时f[Q[head]]即为Max{f[j]|i-R <= j <= i-L},f[i]可直接通过队首位置转移求得,转移代价为O(1)。
    (此解法由Wagner提供,具体实现pas)
    时间复杂度:O(N) 期望得分:100

    #include <iostream>
    using namespace std;
    #define MAXN 200009
    #define Inf 999999999
    bool step[ MAXN ];
    int ice[ MAXN ], prev[ MAXN ], f[ MAXN ];
    int N, L, R;
    struct HEAP
    {
        struct DATA
        {
            int from, key;
        }    heap[ MAXN ];
        int pos[ MAXN ];
        int size;
        void Swap(int x, int y)
        {
            DATA box;
            box = heap[x]; heap[x] = heap[y]; heap[y] = box;
            pos[ heap[x].from ] = x;
            pos[ heap[y].from ] = y;
            return ;
        }
        void HeapUp(int x)
        {
            int tp;
            while (x != 1)
            {
                tp = x >> 1;
                if (heap[x].key < heap[tp].key) break;
                Swap(tp, x);
                x = tp;
            }
            return ;
        }
        void HeapDown(int x)
        {
            int tp;
            while (x * 2 <= size)
            {
                tp = x << 1;
                if (tp < size) 
                    tp += (heap[tp].key < heap[tp + 1].key);
                if (heap[x].key > heap[tp].key) break;
                Swap(tp, x);
                x = tp;
            }
            return ;
        }
        void Insert(int ID)
        {
            ++size;
            heap[ size ].from = ID;
            heap[ size ].key = f[ID];
            pos[ ID ] = size;
            HeapUp( size );
            return ;
        }
        void Delete(int ID)
        {
            int tp = pos[ID];
            if (tp == size)
            {
                size--;
                return ;
            }
            heap[tp] = heap[ size ];
            pos[ heap[tp].from ] = tp;
            size--;
            HeapUp(tp);
            HeapDown(tp);
            return ;
        }
        int Get()
        {
            return heap[1].from;
        }
    }    Heap;
    int main()
    {
        scanf("%d %d %d", &N, &L, &R);
        for (int i = 0; i <= N; i++)
            scanf("%d", &ice[i]);
        for (int i = 1; i < L; i++)
            f[i] = -Inf;
        for (int i = L; i <= N; i++)
        {
            if (i - L >= 0) Heap.Insert(i - L);
            if (i - R - 1 >= 0) Heap.Delete(i - R - 1);
            prev[i] = Heap.Get();
            if (f[ prev[i] ] != -Inf)
                f[i] = f[ prev[i] ] + ice[i];
            else f[i] = -Inf;
        }
        int best = -1;
        for (int i = N - R + 1; i <= N; i++)
            if (best == -1 || f[i] > f[ best ])
                best = i;
        printf("%d\n", f[ best ]);
        do
        {
            step[ best ] = true;
            best = prev[ best ];
        }    while (best);
        step[0] = true;
        for (int i = 0; i <= N; i++)
            if (step[i]) printf("%d ", i);
        printf("-1\n");
        return 0;
    }
    Program iceroad;
    Var
     i,j,k,n,l,r,top,tail,ans:longint;
     a,f,g,q,p:array[0..400000] of longint;
    Begin
     readln(n,l,r); inc(n);
     for i:=1 to n do read(a[i]);
     top:=1; tail:=1; q[1]:=1;
     for i:=2 to n+r do
      begin
       j:=i-r; if j<0 then j:=0;
       k:=i-l; if k<0 then k:=0;
       while (top<=tail)and(i-r>q[top]) do inc(top);
       if (j<=q[top])and(q[top]<=k)
        then begin
         f[i]:=f[q[top]]+a[i];
         g[i]:=q[top];
         if f[i]>=f[ans] then ans:=i;
        end;
       while (top<=tail)and(f[k+1]>f[q[tail]]) do dec(tail);
       inc(tail); q[tail]:=k+1;
      end;
     writeln(f[ans]);
     i:=g[ans]; j:=0;
     while g[i]>0 do
      begin
       inc(j);
       p[j]:=g[i]-1;
       i:=g[i];
      end;
     for j:=j downto 1 do
      write(p[j],' ');
      writeln('-1');
    End.

    Problem 4 上白泽慧音(classroom.cpp/c/pas)
    题目描述 在幻想乡,上白泽慧音是以知识渊博闻名的老师。春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄。因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点。人间之里由N个村庄(编号为1..N)和M条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用1和2来标记。如果存在由村庄A到达村庄B的通路,那么我们认为可以从村庄A到达村庄B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们认为A,B是绝对连通的,记为<A,B>。绝对连通区域是指一个村庄的集合,在这个集合中任意两个村庄X,Y都满足<X,Y>。现在你的任务是,找出最大的绝对连通区域,并将这个绝对连通区域的村庄按编号依次输出。若存在两个最大的,输出字典序最小的,比如当存在1,3,4和2,5,6这两个最大连通区域时,输出的是1,3,4。
    输入格式 第1行:两个正整数N,M
    第2..M+1行:每行三个正整数a,b,t, t = 1表示存在从村庄a到b的单向道路,t = 2表示村庄a,b之间存在双向通行的道路。保证每条道路只出现一次。
    输出格式 第1行: 1个整数,表示最大的绝对连通区域包含的村庄个数。
    第2行:若干个整数,依次输出最大的绝对连通区域所包含的村庄编号。
    输入样例 5 5
    1 2 1
    1 3 2
    2 4 2
    5 1 2
    3 5 1
    输出样例 3
    1 3 5
    数据范围 对于60%的数据:N <= 200且M <= 10,000
    对于100%的数据:N <= 5,000且M <= 50,000

    Classroom 上白泽慧音
    题目大意 给定一张图,存在双向边和单向边若干,求出最大的强连通块。
    考察算法 连通性
    算法一 搜索。根据方法的不同能够得到不同的分值。
    时间复杂度:O(?) 期望得分:0~100
    算法二 通过Floyd判断连通性(F[i][j] = F[i][j] or (F[i][k] and F[k][j])),再将所有的双向连通点对统计出来,进行一次并查集。如果两个点是双向连通点对,把它们归为同一个集合。对于每个集合需要保存它的最小点编号。
    时间复杂度:O(N^3) 期望得分:60
    算法三 通过Tarjan算法求出图中所有的强连通分量,并记录每一个块的最小点编号。最后输出最大连通分量的顶点。
    时间复杂度:O(N+M) 期望得分:100

    #include <iostream>
    using namespace std;
    #define MAXN 5009
    #define MAXM 100009
    int N, M;
    struct TARJAN
    {
        int v[ MAXM ], next[ MAXM ], fir[ MAXN ];
        int dfn[ MAXN ], low[ MAXN ], stack[ MAXN ], tot[ MAXN ], mark[ MAXN ], rep[ MAXN ];
        bool used[ MAXM ], instack[ MAXN ];
        int cnt, top, ecnt, num;
        void DFS(int rt)
        {
            dfn[rt] = low[rt] = ++cnt;
            stack[ ++top ] = rt;
            instack[rt] = true;
            for (int p = fir[rt]; p; p = next[p])
                if (!used[p])
                {
                    used[p] = true;
                    if (!dfn[ v[p] ])
                    {
                        DFS( v[p] );
                        low[rt] = min(low[rt], low[ v[p] ]);
                    }
                    else if (instack[ v[p] ])
                        low[rt] = min(low[rt], dfn[ v[p] ]);
                }
            if (dfn[rt] == low[rt])
            {
                ++num;
                do
                {
                    mark[ stack[ top ] ] = num;
                    instack[ stack[ top-- ] ] = false;
                }    while ( stack[ top + 1 ] != rt);
            }
            return ;
        }
        void Addedge(int rt, int s)
        {
            ++ecnt;
            v[ ecnt ] = s, next[ ecnt ] = fir[rt];
            fir[ rt ]= ecnt;
            return ;
        }
        void Prin(int x)
        {
            bool first = true;
            printf("%d\n", tot[x]);
            for (int i = 1; i <= N; i++)
                if (mark[i] == x)
                {
                    if (!first) printf(" ");
                    first = false;
                    printf("%d", i);
                }
            printf("\n");
            return ;
        }
        void Work()
        {
            for (int i = 1; i <= N; i++)
                if (!dfn[i]) DFS(i);
            for (int i = 1; i <= N; i++)
            {
                tot[ mark[i] ]++;
                if (rep[ mark[i] ] == 0 || rep[ mark[i] ] > i)
                    rep[ mark[i] ] = i;
            }
            int best = -1;
            for (int i = 1; i <= num; i++)
                if (best == -1 || (tot[i] > tot[ best ]) || (tot[i] == tot[ best ] && rep[i] < rep[ best ]))
                    best = i;
            Prin( best );
            return ;
        }
    }    Tarjan;
    int main()
    {
        int u, v, t;
        scanf("%d %d", &N, &M);
        while (M--)
        {
            scanf("%d %d %d", &u, &v, &t);
            Tarjan.Addedge(u, v);
            if (t == 2) Tarjan.Addedge(v, u);
        }
        Tarjan.Work();
        return 0;
    }

    总体来说S1还是很弱的.

  • 相关阅读:
    java基础问题1
    基本数据类型,string类型的瞎扯,final喜欢干的事儿。final string
    关于区块链不懂的东西
    需求更新表属性
    用户体验——响应时间
    后台运行任务nohup xxxxxx &
    jenkins打包maven工程发现有些包下载不下来
    jenkins复选框插件Extended Choice Parameter plugin
    jmeter上传文件tips
    airflow 简介
  • 原文地址:https://www.cnblogs.com/hatsuakiratenan/p/3132492.html
Copyright © 2020-2023  润新知