牛客周赛 Round 57 解题报告 | 珂学家


前言

alt


题解

难度比较适宜,这场周赛出的不错。


A. 小红喜欢1

题型: 签到+语法

arr = list(map(int, input().split()))
 
print (arr.index(1) + 1)

B. 小红的树切割

思路:思维

统计边两端为同一颜色的边数即可

n = int(input())
 
s = input()
 
res = 0
for i in range(n - 1):
    u, v = list(map(int, input().split()))
    if s[u - 1] == s[v - 1]:
        res += 1
         
print (res)

C. 小红的双好数(easy)

思路: 数论 + 分类讨论

首先,2进制是天然好进制

同时需要借用一个经典结论

相邻两个自然数互质,自然 n 满足 ( n − 1 ) 的好进制 相邻两个自然数互质,自然n满足(n - 1)的好进制 相邻两个自然数互质,自然n满足(n1)的好进制

因此,对于大部分n,只要 2 , n − 1 2, n - 1 2,n1两数即可

所以需要考虑的是例外是

  • n = 1,3

    2,3为解

  • n = 2

    无解

n = int(input())

if n == 1 or n == 3:
    print ("YES")
    print (2, 3)
elif n == 2:
    print ("NO")
else:
    print ("YES")
    print (2, n - 1)

D. 小红的线段

思路: 数学 + 贪心

如果和 y = k ∗ x + b , 直线,有交点,则该线段两端点在直线的两侧 如果和 y = k * x + b, 直线,有交点,则该线段两端点在直线的两侧 如果和y=kx+b,直线,有交点,则该线段两端点在直线的两侧

根据 y = f ( x i ) = k ∗ x i + b , 和 y i 进行大小比较 {根据 y= f(x_i) = k * x_i + b, 和 y_i 进行大小比较} 根据y=f(xi)=kxi+b,yi进行大小比较

  • y < y i y \lt y_i y<yi, 则在直线下方, 构建点集 S n e g S_{neg} Sneg
  • y > y i y \gt y_i y>yi, 则在直线上方, 构建点集 S p o s S_{pos} Spos
  • y = = y i {y == y_i} y==yi, 则该点恰好在直线上, 构建点集 S z e r o S_{zero} Szero
  1. 优先利用 S n e g , S p o s S_{neg}, S_{pos} Sneg,Spos构建线段
  2. 次之使用 S z e r o 和多余的 S n e g , S p o s 构建线段 S_{zero} 和 多余的S_{neg}, S_{pos}构建线段 Szero和多余的Sneg,Spos构建线段
  3. 再次之使用 S z e r o 两两配对 S_{zero} 两两配对 Szero两两配对
  4. 剩下的都是没有交点的情况
n, k, b = list(map(int, input().split()))

def xl(x, y):
    y1 = k * x + b
    if y == y1:
        return 0
    elif y > y1:
        return 1
    return -1

pos = []
neg = []
zero = []

for i in range(2 * n):
    x, y = list(map(int, input().split()))
    d = xl(x, y)
    if d==0:
        zero.append(i + 1)
    elif d > 0:
        pos.append(i + 1)
    else:
        neg.append(i + 1)

ans = 0
res = []
while len(pos) > 0 and len(neg) > 0:
    t1, t2 = pos.pop(), neg.pop()
    res.append((t1, t2, 'Y'))
    ans += 1
    
while len(zero) > 0 and len(pos) > 0:
    t1, t2 = zero.pop(), pos.pop()
    res.append((t1, t2, 'Y'))
    ans += 1
    
while len(zero) > 0 and len(neg) > 0:
    t1, t2 = zero.pop(), neg.pop()
    res.append((t1, t2, 'Y'))
    ans += 1
    
while len(zero) >= 2:
    t1, t2 = zero.pop(), zero.pop()
    res.append((t1, t2, 'Y'))
    ans += 1
        
while len(pos) >= 2:
    t1, t2 = pos.pop(), pos.pop()
    res.append((t1, t2, 'N'))
    
while len(neg) >= 2:
    t1, t2 = neg.pop(), neg.pop()
    res.append((t1, t2, 'N'))
        
print (ans)
for e in res:
    print (*e, sep=' ')


E. 小红的双好数(hard)

题型: 数论+枚举

所求的数,满足

z = ∑ a i ∗ k 1 i , a i ∈ ( 0 , 1 ) z = \sum a_i * k1^i, a_i \in (0, 1) z=aik1i,ai(0,1)

同时满足

z = ∑ b i ∗ k 2 i , b i ∈ ( 0 , 1 ) z = \sum b_i * k2^i, b_i \in (0, 1) z=bik2i,bi(0,1)

因为 k 1 < k 2 k1 < k2 k1<k2, 所以尝试构造枚举 { b i ∗ k 2 i } \{b_i * k2^i\} {bik2i}的序列出发,然后验证满足某个 { a j ∗ k 1 j } \{a_j * k1 ^ j\} {ajk1j}

因为 2 ≤ k 1 < k 2 {2 \le k1 \lt k2} 2k1<k2

所以 在 [ 2 , 1 0 18 ] [2, 10^{18}] [2,1018]范围内

满足序列的个数,最多为

2 x , x = l o g ( 1 0 18 ) / l o g ( k 2 ) 2 ^ x, x = log(10^{18}) / log(k2) 2x,x=log(1018)/log(k2)

k2最大的指数状态数
3 l o g 3 1 0 18 = 37.7 {log_3 10^{18} = 37.7} log31018=37.7 2 38 2^{38} 238
4 l o g 4 1 0 18 = 29.8 {log_4 10^{18} = 29.8} log41018=29.8 2 30 2^{30} 230
5 l o g 5 1 0 18 = 25.7 {log_5 10^{18} = 25.7} log51018=25.7 2 26 2^{26} 226
6 l o g 6 1 0 18 = 23.1 {log_6 10^{18} = 23.1} log61018=23.1 2 23 2^{23} 223

分类讨论下

  • k 2 ≤ 6 {k2 \le 6} k26,其实可以打表(实际上不需要,解空间很小)
  • k 2 ≥ 7 {k2 \ge 7} k27, 状态空间已经很小,可暴力全枚举
def check(v, k):
    while v > 0:
        r = v % k
        if r > 1:
            return False
        v //= k
    return True

def solve():
    k1, k2 = list(map(int, input().split()))
    dp = [0]
    base = k2
    while base <= 10 ** 18:
        dp2 = dp[:]
        for v in dp:
            if check(v + base, k1):
                return v + base
            dp2.append(v + base)
        base = base * k2
        dp = dp2
    return -1

ans = solve()
if ans == -1:
    print ("NO")
else:
    print ("YES")
    print (ans)


F. 小红的数组操作

思路: 树套树

不太确定,出题人是否出于这个想法。

但是无论怎么样,线段树中套平衡二叉树,是一种可行的解法。

当然很多同学,在写题的时候,可能会没意识到这点。

前缀最小,更一般化也可以理解为区间最小,那就是RMQ。

这边是区间查询+点更新操作。

// package lc.temp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.StringTokenizer;
import java.util.TreeMap;

public class Main {

    static int inf = Integer.MAX_VALUE;

    static class SegTree {
        int l, r, m;
        int minValue;
        public SegTree left, right;
        public SegTree(int ul, int ur) {
            this.l = ul;
            this.r = ur;
            this.m = (l + r) / 2;
            this.minValue = inf;

            if (l == r) {
                return;
            }
            this.left = new SegTree(ul, m);
            this.right = new SegTree(m + 1, ur);
        }

        void update(int p, int v) {
            if (l == r) {
                this.minValue = v;
                return;
            }
            if (p <= m) {
                this.left.update(p, v);
            } else {
                this.right.update(p, v);
            }
            this.minValue = Math.min(this.left.minValue, this.right.minValue);
        }

        int query(int p) {
            if (l == r) {
                return this.minValue;
            }
            if (p >= r) {
                return this.minValue;
            }
            if (m >= p) {
                return this.left.query(p);
            } else {
                return Math.min(this.left.minValue, this.right.query(p));
            }
        }

    }


    public static void main(String[] args) {
        AReader sc = new AReader();
        int t = 1;
        while (t-- > 0) {
            int n = sc.nextInt();
            TreeMap<Integer, Integer>[] ts = new TreeMap[n];
            int[][] arr = new int[n][];
            for (int i = 0; i < n; i++) {
                int m = sc.nextInt();
                ts[i] = new TreeMap<>();
                arr[i] = new int[m];
                for (int j = 0; j < m; j++) {
                    int v = sc.nextInt();
                    ts[i].merge(v, 1, Integer::sum);
                    arr[i][j] = v;
                }
            }

            SegTree segTree = new SegTree(0, n - 1);
            for (int i = 0; i < n; i++) {
                segTree.update(i, ts[i].firstKey());
            }


            int q = sc.nextInt();
            for (int i = 0; i < q; i++) {
                int op = sc.nextInt();
                if (op == 1) {
                    int x = sc.nextInt(), y = sc.nextInt();
                    x--; y--;
                    int v = sc.nextInt();

                    int ov = arr[x][y];
                    if (ov == v) continue;

                    ts[x].computeIfPresent(ov, (a, b) -> b > 1 ? b - 1: null);
                    arr[x][y] = v;
                    ts[x].merge(v, 1, Integer::sum);

                    segTree.update(x, ts[x].firstKey());
                } else {
                    int x = sc.nextInt();
                    x--;
                    int res = segTree.query(x);
                    System.out.println(res);
                }
            }
        }

    }

    static
    class AReader {
        private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        private StringTokenizer tokenizer = new StringTokenizer("");
        public String nextLine() {
            tokenizer = new StringTokenizer("");
            return innerNextLine();
        }
        public String next() {
            hasNext();
            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }
        public long nextLong() {
            return Long.parseLong(next());
        }
        private String innerNextLine() {
            try {
                return reader.readLine();
            } catch (IOException ex) {
                return null;
            }
        }
        public boolean hasNext() {
            while (!tokenizer.hasMoreTokens()) {
                String nextLine = innerNextLine();
                if (nextLine == null) {
                    return false;
                }
                tokenizer = new StringTokenizer(nextLine);
            }
            return true;
        }
    }

}

唯一的其他想法是,是否还可以借助其他数据结构实现?


G. 小红的双排列构造

思路: 构造

其实这题的指向很明确

假设存在一个 1   n 的排列 假设存在一个1~n的排列 假设存在一个1 n的排列

然后把剩下的1~n围绕其左右侧,使得正好的排列数为K

策略:

  • 先左侧构建一个 1~N的数组

  • 那右侧天然就是一个1~N的数组

  • 然后从左侧,依次构造 k - 2的排列数

  • 剩下的何如阻断呢?其实打断它的循环节即可


对于特殊的情况,比如k=0,n=1,这些组合需要特判

写了一个很奇怪的代码

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(new BufferedInputStream(System.in));
        int n = sc.nextInt(), k = sc.nextInt();

        if (k == 0) {
            if (n <= 2) {
                System.out.println("-1");
            } else {
                List<Integer> res = new ArrayList<>();
                for (int i = 0; i < n; i++) {
                    res.add(i + 1);
                    res.add(i + 1);
                }
                System.out.println(res.stream().map(String::valueOf).collect(Collectors.joining(" ")));
            }
        } else {
            if (n == 1) {
                if (k == 1) {
                    System.out.println(-1);
                } else if (k==2) {
                    System.out.println("1 1");
                }
            } else if (k == 1) {
                List<Integer> res = new ArrayList<>();
                res.add(1);
                for (int i = 0; i < n; i++) {
                    res.add(i + 1);
                }
                for (int i = 1; i < n; i++) {
                    res.add(i + 1);
                }
                System.out.println(res.stream().map(String::valueOf).collect(Collectors.joining(" ")));
            } else {
                List<Integer> res = new ArrayList<>();
                for (int i = 0; i < n; i++) {
                    res.add(i + 1);
                }
                for (int i = 0; i < k - 2; i++) {
                    res.add(i + 1);
                }
                for (int i = k - 2; i < n; i++) {
                    res.add(n - (i - k + 2));
                }
                System.out.println(res.stream().map(String::valueOf).collect(Collectors.joining(" ")));
            }
        }
    }
}

写在最后

alt

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值