第12届蓝桥杯JavaB组省赛

第12届蓝桥杯JavaB组省赛

其他链接

第9届蓝桥杯JavaB组省赛 - Cattle_Horse

第10届蓝桥杯JavaB组省赛 - Cattle_Horse

第11届蓝桥杯JavaB组省赛 - Cattle_Horse

第13届蓝桥杯javaB组省赛 - Cattle_Horse

前言

用时及分数

自测开始时间:\(2023年1月11日 16:00\)

自测结束时间:\(2023年1月11日 18:05\)

自测用时:约 \(2\) 小时

最终得分:\(20(35)\)

PS:此处得分依据蓝桥杯官网 \(OJ\) 测得

题目情况

完成题目:\(ABC(F)\)

\(F\) 题提交时的类名未使用 \(Main\),不得分。

\(D\) 题暴力写法跑了 \(30\) 分钟没跑出来

因为干饭提前结束测试

\(E\) 最短路没学或者说忘了

感受

\(12\) 届的题目填空题居然有 \(5\) 道,\(13\) 届才 \(2\)

而且感觉 \(13\) 届比 \(12\) 届更偏向算法知识了

注意点

  • 提交时一定要注意类名等情况,或者建文件时就建多个 \(Main\)

  • 心态放平,时间很充裕

  • 实在写不出来正解,也尽量把暴力写出来

  • 一定要把题目都看完

  • 填空题提交之前一定要先对小数据进行测试

试题 A ASC

问题描述

已知大写字母 \(A\)\(ASCII\) 码为 \(65\),请问大写字母 \(L\)\(ASCII\) 码是多少?

答案为:\(76\)

Code

public class Main {
    public static void main(String[] args) {
        System.out.println((int) ('L'));//76
    }
}

试题 B 卡片

问题描述

小蓝有很多数字卡片,每张卡片上都是数字 \(0\)\(9\)

小蓝准备用这些卡片来拼一些数,他想从 \(1\) 开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。

小蓝想知道自己能从 \(1\) 拼到多少。

例如,当小蓝有 \(30\) 张卡片,其中 \(0\)\(9\)\(3\) 张,则小蓝可以拼出 \(1\)\(10\),但是拼 \(11\) 时卡片 \(1\) 已经只有一张了,不够拼出 11。

现在小蓝手里有 \(0\)\(9\) 的卡片各 \(2021\) 张,共 \(20210\) 张,请问小蓝可以从 \(1\) 拼到多少?

提示:建议使用计算机编程解决问题。

答案为:\(3181\)

思路

一个数组记录当前对应类型的牌剩余多少张,每算一个数字就减去所需的牌数

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[] arr = new int[10];
        Arrays.fill(arr, 2021);
        int now = 1;
        while (true) {
            int t = now;
            while (t != 0) {
                if (arr[t % 10] > 0) --arr[t % 10];
                else {
                    //这里要输出now-1,因为当前这个牌是拼不出来的!!!
                    System.out.println(now-1);//3181
                    return;
                }
                t /= 10;
            }
            ++now;
        }
    }
}

试题 C 直线

问题描述

在平面直角坐标系中,两点可以确定一条直线。如果有多点在一条直线上,那么这些点中任意两点确定的直线是同一条。

给定平面上 \(2 × 3\) 个整点 \(\{(x, y)|0 ≤ x < 2, 0 ≤ y < 3, x ∈ Z, y ∈ Z\}\),即横坐标是 \(0\)\(1\) (包含 \(0\)\(1\)) 之间的整数、纵坐标是 \(0\)\(2\) (包含 \(0\)\(2\)) 之间的整数的点。这些点一共确定了 \(11\) 条不同的直线。

给定平面上 \(20 × 21\) 个整点 \(\{(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z\}\),即横坐标是 \(0\)\(19\) (包含 \(0\)\(19\)) 之间的整数、纵坐标是 \(0\)\(20\) (包含 \(0\)\(20\)) 之间的整数的点。请问这些点一共确定了多少条不同的直线。

答案为:\(40257\)

思路

既然是填空题,比赛的时候能暴力解就暴力解

既能很快的得到答案,也能减少出错的概率

具体思路见注释

import java.util.LinkedList;
import java.util.List;

public class Main {
    static class Point {
        public int x, y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    static class Line {

        public Point A, B;

        public Line(Point A, Point B) {
            this.A = A;
            this.B = B;
        }
    }

    static List<Line> list = new LinkedList<>();

    //判断点是否在该直线上
    public static boolean checkPoint(Point p, Line l) {
        return (p.y - l.A.y) * (p.x - l.B.x) == (p.y - l.B.y) * (p.x - l.A.x);
    }

    //判断两条直线是否为同一条
    public static boolean checkLine(Line a, Line b) {
        //先判断斜率是否相等
        if ((a.B.y - a.A.y) * (b.B.x - b.A.x) != (b.B.y - b.A.y) * (a.B.x - a.A.x)) return false;
        //如果斜率相等再取直线上一点,是否在另一直线上
        return checkPoint(a.A, b);
    }

    //判断列表中是否存在该直线
    public static boolean check(Line a) {
        for (Line l : list) {
            if (checkLine(a, l)) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        final int X = 20, Y = 21;
        for (int x1 = 0; x1 < X; ++x1) {
            for (int y1 = 0; y1 < Y; ++y1) {
                Point a = new Point(x1, y1);
                for (int x2 = 0; x2 < X; ++x2) {
                    for (int y2 = 0; y2 < Y; ++y2) {
                        //如果两点重合则不选
                        if (x2 == x1 && y2 == y1) continue;
//                        System.out.printf("a=(%d,%d),b=(%d,%d)\n", x1, y1, x2, y2);
                        Point b = new Point(x2, y2);
                        Line l = new Line(a, b);
                        if (!check(l)) list.add(l);
                    }
                }
            }
        }
        System.out.println(list.size());//40257
    }
}

试题 D 货物摆放

问题描述

小蓝有一个超大的仓库,可以摆放很多货物。

现在,小蓝有 \(n\) 箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。

小蓝希望所有的货物最终摆成一个大的立方体。即在长、宽、高的方向上分别堆 \(L、W、H\) 的货物,满足 \(n = L \times W \times H\)

给定 \(n\),请问有多少种堆放货物的方案满足要求。

例如,当 \(n = 4\) 时,有以下 \(6\) 种方案:\(1×1×4、1×2×2、1×4×1、2×1×2、2 × 2 × 1、4 × 1 × 1\)

请问,当 \(n = 2021041820210418\) (注意有 \(16\) 位数字)时,总共有多少种方案?

提示:建议使用计算机编程解决问题。

答案为:\(2430\)

思路一

遍历第一个数 \(L\),如果是 \(n\) 的因子,说明当前数可取,然后遍历第二个数 \(W\)

由于 \(n = L × W × H\),当知道前两个数时,第 \(3\) 个数也随之确定

\(W\ne H\) 时,\(W\) 的值与 \(H\) 的值交换算是两种方案

因此,只需确定 \(W< \sqrt{\dfrac{n}{L}}\)\(W\) 取值数量,然后乘以 \(2\)

再单独确定 \(W=\sqrt{\dfrac{n}{L}}\) 时是否满足条件,此时只算做一种方案

程序运行时间很长,最后没跑出来

public class Main {
    public static void main(String[] args) {
        final long n = 2021041820210418L;
        long ans = 0;
        for (long l = 1; l <= n; ++l) {
            if (n % l != 0) continue;
            final long rest = n / l;
            long w;
            for (w = 1; w * w < rest; ++w) {
                if (rest % w == 0) ans += 2;
            }
            if (w * w == rest) ++ans;
        }
        System.out.println(ans);
    }
}

思路二

比赛时最开始想到的思路

先处理出 \(n\) 的所有因子,再三重循环(或 \(DFS\))选出三个因子,相乘是否等于 \(n\)

public class Main {
    public static void main(String[] args) {
        long[] num = new long[1000];
        int cnt = 0;
        final long n = 2021041820210418L;
        long now = 1;
        while (now * now < n) {
            if (n % now == 0) {
                num[cnt++] = now;
                num[cnt++] = n / now;
            }
            ++now;
        }
        if (now * now == n) nums[cnt++] = now;
        long ans = 0;
        for (int i = 0; i < cnt; ++i) {
            for (int j = 0; j < cnt; ++j) {
                for (int k = 0; k < cnt; ++k) {
                    if (num[i] * num[j] * num[k] == n) {
                        ++ans;
                    }
                }
            }
        }
        System.out.println(ans);//2430
    }
}

判断优化

只需遍历两个因子,如果这两个因子的积仍为 \(n\) 的因子,则此时是一种方案

public class Main {
    public static void main(String[] args) {
        long[] num = new long[1000];
        int cnt = 0;
        final long n = 2021041820210418L;
        long now = 1;
        while (now * now < n) {
            if (n % now == 0) {
                num[cnt++] = now;
                num[cnt++] = n / now;
            }
            ++now;
        }
        if (now * now == n) nums[cnt++] = now;
        long ans = 0;
        for (int i = 0; i < cnt; ++i) {
            for (int j = 0; j < cnt; ++j) {
                if (n % (num[i] * num[j]) == 0) ++ans;
            }
        }
        System.out.println(ans);//2430
    }
}

思路三

\(L\leq W\leq H\),则:

  1. \(L=W=H\) 时,生成方案数为 \(1\)
  2. \(L=W < H\)\(L< W=H\) 时,生成方案数为 \(3\)
  3. \(L<W<H\) 时,生成方案数为 \(6\)
public class Main {
    public static void main(String[] args) {
        final long n = 2021041820210418L;
        long ans = 0;
        for (long i = 1; i * i * i <= n; ++i) {
            if (n % i != 0) continue;
            long rest = n / i;
            for (long j = i; j * j <= rest; ++j) {
                if (rest % j != 0) continue;
                long k = rest / j;
                if (i < j && j < k) ans += 6;
                else if (i == j && j == k) ans += 1;
                else ans += 3;
            }
        }
        System.out.println(ans);//2430
    }
}

试题 E 路径

问题描述

小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。

小蓝的图由 \(2021\) 个结点组成,依次编号 \(1\)\(2021\)

对于两个不同的结点 \(a, b\),如果 \(a\)\(b\) 的差的绝对值大于 \(21\),则两个结点之间没有边相连;

如果 \(a\)\(b\) 的差的绝对值小于等于 \(21\),则两个点之间有一条长度为 \(a\)\(b\) 的最小公倍数的无向边相连。

例如:结点 \(1\) 和结点 \(23\) 之间没有边相连;结点 \(3\) 和结点 \(24\) 之间有一条无向边,长度为 \(24\);结点 \(15\) 和结点 \(25\) 之间有一条无向边,长度为 \(75\)

请计算,结点 \(1\) 和结点 \(2021\) 之间的最短路径长度是多少。

提示:建议使用计算机编程解决问题。

答案为:\(10266837\)

思路

前置知识:

  1. 最短路 - Cattle_Horse

\(Floyd\) 求全源最短路

import java.util.Arrays;

public class Main {
    static int gcd(int x, int y) {
        return y == 0 ? x : gcd(y, x % y);
    }

    static int lcm(int x, int y) {
        return x / gcd(x, y) * y;
    }

    static final int N = 2021;
    static final int INF = (int) 1e9;
    //dis[i][j]表示i->j的路径长度
    static int[][] dis = new int[N + 1][N + 1];

    //运行Floyd之后,dis[i][j]变为 i->j的最短路径长度
    static void Floyd() {
        for (int k = 1; k <= N; ++k)
            for (int i = 1; i <= N; ++i)
                for (int j = 1; j <= N; ++j)
                    dis[i][j] = Math.min(dis[i][j], dis[i][k] + dis[k][j]);
    }

    public static void main(String[] args) {
        for (int i = 0; i <= N; ++i) Arrays.fill(dis[i], INF);
        for (int i = 1; i <= N; ++i) {
            for (int j = i + 1; j <= N; ++j) {
                if (j - i <= 21) {
                    dis[i][j] = dis[j][i] = lcm(i, j);
                }
            }
        }
        Floyd();
        System.out.println(dis[1][2021]);//10266837
    }
}

\(Dijkstra\) 求单源最短路

import java.util.Arrays;
import java.util.LinkedList;
import java.util.PriorityQueue;

public class Main {
    static class edge {
        int v, w;

        public edge(int v, int w) {
            this.v = v;
            this.w = w;
        }
    }


    static final int N = 2021;
    static final int INF = 0x3f3f3f3f;
    static LinkedList<edge>[] adj;
    static int[] dis = new int[N + 1];
    static boolean[] vis = new boolean[N + 1];

    static void Dijkstra(int start) {

        class node {
            int v, dis;

            public node(int v, int dis) {
                this.v = v;
                this.dis = dis;
            }
        }
        //小根堆
        PriorityQueue<node> q = new PriorityQueue<node>((o1, o2) -> o1.dis - o2.dis);
        Arrays.fill(dis, INF);
        dis[start] = 0;
        q.add(new node(start, 0));
        while (!q.isEmpty()) {
            int u = q.poll().v;
            if (vis[u]) continue;
            vis[u] = true;
            for (edge e : adj[u]) {
                if (dis[e.v] > dis[u] + e.w) {
                    dis[e.v] = dis[u] + e.w;
                    q.add(new node(e.v, dis[e.v]));
                }
            }
        }
    }

    public static void main(String[] args) {
        adj = new LinkedList[N + 1];
        for (int i = 1; i <= N; ++i) adj[i] = new LinkedList<edge>();
        for (int i = 1; i <= N; ++i) {
            for (int j = i + 1; j <= N; ++j) {
                if (j - i <= 21) {
                    int LCM = lcm(i, j);
                    adj[i].add(new edge(j, LCM));
                    adj[j].add(new edge(i, LCM));
                }
            }
        }
        Dijkstra(1);
        System.out.println(dis[N]);//10266837
    }

    static int gcd(int x, int y) {
        return y == 0 ? x : gcd(y, x % y);
    }

    static int lcm(int x, int y) {
        return x / gcd(x, y) * y;
    }
}

试题 F 时间显示

问题描述

小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从 \(1970\)\(1\)\(1\)\(00:00:00\) 到当前时刻经过的毫秒数。

现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需要显示出时分秒即可,毫秒也不用显示,直接舍去即可。

给定一个用整数表示的时间,请将这个时间对应的时分秒输出。

【输入格式】

输入一行包含一个整数,表示时间。

【输出格式】

输出时分秒表示的当前时间,格式形如 \(HH:MM:SS\),其中 \(HH\) 表示时,值为 \(0\)\(23\)\(MM\) 表示分,值为 \(0\)\(59\)\(SS\) 表示秒,值为 \(0\)\(59\)

时、分、秒不足两位时补前导 0。

【样例输入 1】
46800999
【样例输出 1】
13:00:00
    
【样例输入 2】
1618708103123
【样例输出 2】
01:08:23

【评测用例规模与约定】

对于所有评测用例,给定的时间为不超过 \(10^{18}\) 的正整数

Code

import java.util.Scanner;

public class F {
    //计算一天有多少毫秒
    static final long dayMS = 24 * 60 * 60 * 1000;

    public static void main(String[] args) {
        long n = new Scanner(System.in).nextLong() % dayMS;
        long h = n / (60 * 60 * 1000);
        long m = n / (60 * 1000) % 60;
        long s = n / 1000 % 60;
        System.out.println(String.format("%02d:%02d:%02d", h, m, s));
    }
}

试题 G 最少砝码

问题描述

你有一架天平。现在你要设计一套砝码,使得利用这些砝码可以称出任意小于等于 \(N\) 的正整数重量。

那么这套砝码最少需要包含多少个砝码?

注意砝码可以放在天平两边。

思路

推导

顺着推导由多少个砝码组合出 \(1\sim N\) 这些正整数不是很好想。

试着逆着推导,\(x\) 个砝码能组合出的最大的 \(1\sim n\)

考虑多加 \(1\) 个砝码对前一种情况的影响。

假设有 \(1\) 个砝码,它的重量是 \(x\),新加入一个砝码,它的重量是 \(y\),那么能表达的数字分别是 \(\{x,y,|y-x|,y+x\}\),要使能组合的从 \(1\) 开始的连续的数最多,那么 \(\{x,y,|y-x|,y+x\}\) 应该互不相同且连续。

因为 \(y\) 本身能表达一个数字,要使他表达的数最多,则对于新加入的 \(y\) 应该大于 \(x\),因此 \(\{x,y,y-x,y+x\}\)

再加入一个新的砝码,它的重量是 \(z\),那么能表达的数字是 \(\{\{x,y,y-x,y+x\},z+\{x,y,y-x,y+x\},z-\{x,y,y-x,y+x\},z\}\)
同上,\(z>y>x\),且要使能组合的从 \(1\) 开始的连续的数最多,则能表达的数字应该互不相同且连续。

示例

例如:

  1. \(1\) 个砝码,组合出的最大的 \(1\sim n\)\(1\sim 1\)

  2. \(2\) 个砝码,设加入重量为 \(x\) 的砝码(\(x>1\)),组合出的数为 \(\{1,x,x-1,x+1\}\)
    要使其互不相同且连续,则 \(min(x,x-1,x+1)=1+1=2\),即 \(x-1=2\),也即 \(x=3\)
    因此组合出 \(1\sim max(x,x-1,x+1)=x+1\),即 \(1\sim 4\)

  3. \(3\) 个砝码,设加入的重量为 \(x\) 的砝码(\(x>4\)),组合出的数为 \(\{\{1,2,3,4\},x+\{1,2,3,4\},x-\{1,2,3,4\},x\}\)
    要使其互不相同且连续,则 \(min(x+\{1,2,3,4\},x-\{1,2,3,4\},x)=4+1=5\),即 \(x-4=5\),也即 \(x=9\)
    因此组合出 \(1\sim max(x+\{1,2,3,4\},x-\{1,2,3,4\},x)=x+4\),即 \(1\sim 13\)

  4. \(k\) 个砝码,设其最多能组合出 \(1\sim n_k\),则加入第 \(k+1\) 个砝码,设其重量为 \(x\),组合出的数为 \(\{\{1\sim n_k\},x+\{1\sim n_k\},x-\{1\sim n_k\},x\}\)

    要使其互不相同且连续,则 \(min(x+\{1\sim n_k\},x-\{1\sim n_k\},x)=n_k+1\),即 \(x-n_k=n_k+1\) ,也即 \(x=2\times n_k+1\)

    因此组合出 \(1\sim max(x+\{1\sim n_k\},x-\{1\sim n_k\},x)=x+n_k\),即 \(1\sim 3\times n_k+1\)

总结

  • 顺着题意推导可能没有思路的时候,可以考虑逆着推导
  • 利用 前面有的结果
  • 动态规划中常用前缀和等一些 \(1\sim n\) 前连续个数的某种性质

Code

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        int N = new Scanner(System.in).nextInt();
        int k = 1, n = 1;//k为砝码个数,n为能表示的最大的连续数
        while (n < N) {
            ++k;
            n = 3 * n + 1;
        }
        System.out.println(k);
    }
}

试题 H 杨辉三角形

问题描述

下面的图形是著名的杨辉三角形:

如果我们按从上到下、从左到右的顺序把所有数排成一列,可以得到如下数列:
\(1, 1, 1, 1, 2, 1, 1, 3, 3, 1, 1, 4, 6, 4, 1,\cdots\)
给定一个正整数 \(N\),请你输出数列中第一次出现 \(N\) 是在第几个数?
【输入格式】

输入一个整数 \(N\)

【输出格式】

输出一个整数代表答案。

【样例输入】
\(6\)
【样例输出】
\(13\)

【评测用例规模与约定】

对于 \(20\%\) 的评测用例,\(1 ≤ N ≤ 10\)
对于所有评测用例,\(1 ≤ N ≤ 1000000000\)

时间限制: \(5.0s\) 内存限制: \(512.0MB\)

思路

常规的杨辉三角形计算及输出如下:

final int N = 30;
int[][] triangle = new int[N + 1][N + 1];
for (int i = 1; i <= N; ++i) {
    triangle[i][1] = 1;
    for (int j = 2; j < i; ++j) triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
    triangle[i][i] = 1;
}
for (int i = 1; i <= N; ++i) {
    for (int j = 1; j <= i; ++j) {
        System.out.print(triangle[i][j] + " ");
    }
    System.out.println();
}

通过对杨辉三角数据的观察发现:

对于正整数 \(N\),在第 \(N+1\)\(2\) 列一定会出现,因此最大范围应为 \([N+1][N+1]\)

\(N_{max}=10^9\) 很明显会超出内存限制,考虑内存优化

对于式子 \(triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j]\) 只用到了前一行的两个数据,而前面的数据都是不再使用了的,所以采用滚动数组。

如果内循环仍从小到大更新,则当计算 \(triangle[j]\) 时,\(triangle[j-1]\) 已经被更新了,因此需要将前一行的数据记录。

final int N = 30;
int[][] triangle = new int[2][N + 1];
triangle[0][1] = triangle[1][1] = 1;
for (int i = 1; i <= N; ++i) {
    System.out.print(1 + " ");
    for (int j = 2; j <= i; ++j) {
        triangle[i % 2][j] = triangle[(i - 1) % 2][j - 1] + triangle[(i - 1) % 2][j];
        System.out.print(triangle[i % 2][j] + " ");
    }
    System.out.println();
}

更推荐逆序遍历,当计算 \(triangle[j]\)时,\(triangle[j-1]\) 仍为前一行的数据(与 \(01\) 背包相同)

final int N = 30;
int[] triangle = new int[N + 1];
triangle[1] = 1;
for (int i = 1; i <= N; ++i) {
    for (int j = i; j >= 2; --j) triangle[j] += triangle[j - 1];
    for (int j = 1; j <= i; ++j) System.out.print(triangle[j] + " ");
    System.out.println();
}

杨辉三角,是二项式系数在三角形中的一种几何排列。

\(n\)\(m\) 列元素通项公式为: \(C(n-1,m-1)=\dfrac{(n-1)!}{(m-1)!\times(n-m)!}\)

\(triangle[i][3]>N\) 时,对于 \(k>i\)\(j\) 为该行第 \(3\) 列到倒数第三列之间的列数,\(triangle[k][j]\) 一定也大于 \(N\)

如果在此之前的数均不为 \(N\),那么 \(N\) 一定在 \((N+1,2)\) 处取得,即 \(ans=\dfrac{(1+N)\times N}{2}+2\)

\(\begin{alignat}{1} C(n-1,3-1)&=C(n-1,2)\\ &=\dfrac{(n-1)!}{2\times(n-3)!}\\ &=\dfrac{(n-1)\times(n-2)}{2}\\ &<\dfrac{n^2}{2} \end{alignat} \)

则当 \(C(n-1,3-1)>N\) 时,\(n>\sqrt{2\times N}\),因此数组范围大于 \(\sqrt{2\times N}\) 即可

Code(90%)

import java.util.Scanner;

public class Main {
    static long[] triangle;

    public static void main(String[] args) {
        final int N = new Scanner(System.in).nextInt();
        if (N == 1) {
            System.out.println(1);
            return;
        }
        triangle = new long[(int) Math.sqrt(N)*2];
        int ans = 1;
        triangle[0] = 1;
        for (int i = 1; i <= N; ++i) {
            for (int j = i; j >= 1; --j) {
                triangle[j] += triangle[j - 1];
                if (triangle[j] == N) {
                    System.out.println(ans + i-j+ 1);
                    return;
                }
            }
            if (triangle[2] > N) break;
            ans += i + 1;
        }
        System.out.println(((1L + N) * N) / 2 + 2);
    }
}

效率优化

由于杨辉三角是关于 \(y\) 轴对称的,因此可以只遍历一半的数据,要注意特殊处理偶数行的最大值

import java.util.Scanner;

public class Main {
    static long[] triangle;

    public static void main(String[] args) {
        final int N = new Scanner(System.in).nextInt();
        if (N == 1) {
            System.out.println(1);
            return;
        }
        triangle = new long[(int) Math.sqrt(N)*2];
        int ans = 1;
        triangle[0] = 1;
        for (int i = 1; i <= N; ++i) {
            if (i % 2 == 0) triangle[i / 2] = triangle[i / 2 - 1];
            for (int j = i / 2; j >= 1; --j) {
                triangle[j] += triangle[j - 1];
                if (triangle[j] == N) {
                    System.out.println(ans + j + 1);
                    return;
                }
            }
            if (triangle[2] > N) break;
            ans += i + 1;
        }
        System.out.println(((1L + N) * N) / 2 + 2);
    }
}

试题 I 双向排序

问题描述

给定序列 \((a_1 , a_2,\cdots, a_n) = (1, 2,\cdots, n)\),即 \(a_i = i\)
小蓝将对这个序列进行 \(m\) 次操作,每次可能是将 \(a_1 , a_2 ,\cdots, a_{q_i}\) 降序排列, 或者将 \(a_{q_i} , a_{q_i+1} , · · · , a_n\) 升序排列。
请求出操作完成后的序列。
【输入格式】
输入的第一行包含两个整数 \(n, m\),分别表示序列的长度和操作次数。
接下来 \(m\) 行描述对序列的操作,其中第 \(i\) 行包含两个整数 \(p_i , q_i\) 表示操作
类型和参数。当 $p_i = 0 $ 时,表示将 \(a_1 , a_2 , \cdots , a_{q_i}\) 降序排列;当 \(p_i = 1\) 时,表示 将 \(a_{q_i} , a_{q_i+1} , · · · , a_n\) 升序排列。
【输出格式】
输出一行,包含 \(n\) 个整数,相邻的整数之间使用一个空格分隔,表示操作
完成后的序列。

【样例输入】
3 3
0 3
1 2
0 2
【样例输出】
3 1 2

思路(60%)

不会写,交暴力

时间复杂度 \(O(m\times n\times log(n))\)

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt();
        Integer[] a = new Integer[n + 1];
        for (int i = 1; i <= n; ++i) a[i] = i;
        for (int i = 0; i < m; ++i) {
            int p = sc.nextInt(), q = sc.nextInt();
            if (p == 1) Arrays.sort(a, q, n + 1);
            else Arrays.sort(a, 1, q + 1, Collections.reverseOrder());
        }
        for (int i = 1; i <= n; ++i) System.out.print(a[i] + " ");
    }
}

posted @ 2023-01-11 21:29  Cattle_Horse  阅读(150)  评论(0编辑  收藏  举报