前言
题解
难度比较适宜,这场周赛出的不错。
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满足(n−1)的好进制
因此,对于大部分n,只要 2 , n − 1 2, n - 1 2,n−1两数即可
所以需要考虑的是例外是
-
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=k∗x+b,直线,有交点,则该线段两端点在直线的两侧
根据 y = f ( x i ) = k ∗ x i + b , 和 y i 进行大小比较 {根据 y= f(x_i) = k * x_i + b, 和 y_i 进行大小比较} 根据y=f(xi)=k∗xi+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
- 优先利用 S n e g , S p o s S_{neg}, S_{pos} Sneg,Spos构建线段
- 次之使用 S z e r o 和多余的 S n e g , S p o s 构建线段 S_{zero} 和 多余的S_{neg}, S_{pos}构建线段 Szero和多余的Sneg,Spos构建线段
- 再次之使用 S z e r o 两两配对 S_{zero} 两两配对 Szero两两配对
- 剩下的都是没有交点的情况
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=∑ai∗k1i,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=∑bi∗k2i,bi∈(0,1)
因为 k 1 < k 2 k1 < k2 k1<k2, 所以尝试构造枚举 { b i ∗ k 2 i } \{b_i * k2^i\} {bi∗k2i}的序列出发,然后验证满足某个 { a j ∗ k 1 j } \{a_j * k1 ^ j\} {aj∗k1j}
因为 2 ≤ k 1 < k 2 {2 \le k1 \lt k2} 2≤k1<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} k2≤6,其实可以打表(实际上不需要,解空间很小)
- k 2 ≥ 7 {k2 \ge 7} k2≥7, 状态空间已经很小,可暴力全枚举
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(" ")));
}
}
}
}