文章目录
- [A - Past ABCs](https://atcoder.jp/contests/abc350/tasks/abc350_a)
- [B - Dentist Aoki](https://atcoder.jp/contests/abc350/tasks/abc350_b)
- [C - Sort](https://atcoder.jp/contests/abc350/tasks/abc350_a)
- [D - New Friends](https://atcoder.jp/contests/abc350/tasks/abc350_d)
- [E - Toward 0](https://atcoder.jp/contests/abc350/tasks/abc350_e)
- 模板
Note: 省略模版代码
A - Past ABCs
题目:
给一个以 ABC 开头的长度为6的字符串,最后三个字符是数字,要求数字大于0且小于350,不包含316
思路:
取子字符串判断即可
static void solve() throws IOException {
String ins = in.readLine();
int n = pInt(ins.substring(3));
if (n > 0 && n < 350 && n != 316) {
out.println("Yes");
} else {
out.println("No");
}
}
B - Dentist Aoki
题目:
Takahashi有N个牙齿,从1到N标号,牙医Aok执行Q次治疗:
- 如果牙洞上有牙齿,拔掉
- 如果牙洞上没有牙齿,则种一颗牙
求Q次治疗之后Takahashi有多少个牙齿
吐槽:好残忍啊
思路:
记录一下是否有牙齿,实时维护数量即可
static void solve() throws IOException {
String[] ins = pStringArray();
int n = pInt(ins[0]), q = pInt(ins[1]);
int ans = n;
int[] op = pIntArray(0);
boolean[] vis = new boolean[n + 1];
for (int i = 0; i < q; i ++) {
if (vis[op[i]]) {
ans ++;
vis[op[i]] = false;
} else {
ans --;
vis[op[i]] = true;
}
}
out.println(ans);
}
C - Sort
题目:
给一个从1到N的排列A,执行若干次(0与N-1次之间)操作,每次操作选取一对下标进行交换,要求操作执行完后A为单调递增,
输出所选取的下标对
思路:
对于当前位置 i
,对应的值肯定是 i
,如果不是 i
那么就需要将当前位置上的 A[i]
放到其应该回到的位置(下标为A[i]
)
static void solve() throws IOException {
int n = pInt(in.readLine());
int[] p = pIntArray(1);
List<int[]> ans = new ArrayList<>();
for (int i = 1; i <= n; i ++) {
while (p[i] != i) {
if (p[i] < i) {
ans.add(new int[] {p[i], i});
} else {
ans.add(new int[] {i, p[i]});
}
int t = p[i];
p[i] = p[p[i]];
p[t] = t;
}
}
out.println(ans.size());
for (int[] x : ans) {
out.println(x[0] + " " + x[1]);
}
}
D - New Friends
题目:
有N个用户,编号1到N,两个用户可以成为朋友,朋友关系是双向(无向边),现在有M对朋友关系,求能够执行以下操作的最大次数:选取三个用户,X和Y是朋友,Y和Z是朋友,Z和X不是朋友,使X和Z成为朋友;
思路:
首先,通过样例能够知道,执行完操作后,当前连通块会成为一个完全连通图;那么当前连通块的最大执行次数就是:完全连通图的边数减去当前连通块的边数;完全连通块的边数为(连通块的点数为x)
x
⋅
(
x
−
1
)
2
\frac{x \cdot (x - 1)}{2}
2x⋅(x−1);
那使用 并查集 维护每个连通块的结点数以及边数,就能够求出每个连通块的最大执行次数,最后将这些加起来就行了
// 并查集
static class UnionFind {
final int[] p;
// 结点数量
final int[] cnt;
// 边数量
final long[] edges;
public UnionFind(int n) {
p = new int[n];
cnt = new int[n];
edges = new long[n];
Arrays.fill(cnt, 1);
Arrays.setAll(p, idx -> idx);
}
public int find(int x) {
if (p[x] != x) {
p[x] = find(p[x]);
}
return p[x];
}
public void merge(int x, int y) {
int px = find(x), py = find(y);
edges[py] += 1;
if (px != py) {
p[px] = py;
cnt[py] += cnt[px];
edges[py] += edges[px];
}
}
public int getCnt(int x) {
return cnt[find(x)];
}
public long getEdgeCnt(int x) {
return edges[find(x)];
}
}
static void solve() throws IOException {
String[] ins = pStringArray();
int n = pInt(ins[0]), m = pInt(ins[1]);
UnionFind uf = new UnionFind(n + 1);
while (m -- > 0) {
ins = pStringArray();
int u = pInt(ins[0]), v = pInt(ins[1]);
uf.merge(u, v);
}
long ans = 0;
Set<Integer> vis = new HashSet<>();
for (int i = 1; i <= n ; i ++) {
if (!vis.contains(uf.find(i))) {
vis.add(uf.find(i));
int cnt = uf.getCnt(i);
long count = (long) cnt * (cnt - 1) / 2;
ans += count - uf.getEdgeCnt(uf.find(i));
}
}
out.println(ans);
}
E - Toward 0
题目:
给一个整数N,可以执行两种操作:
- 将N替代为 ⌊ N A ⌋ \left \lfloor \frac{N}{A} \right \rfloor ⌊AN⌋,代价为X
- 投6面的骰子的结果为B,将N替代为 ⌊ N B ⌋ \left \lfloor \frac{N}{B} \right \rfloor ⌊BN⌋,代价为Y
求能够使N变成0最小的期望代价;
思路:
N替代为
⌊
N
A
⌋
\left \lfloor \frac{N}{A} \right \rfloor
⌊AN⌋ 时是一种状态转化,可以规定一种状态:f(n)
为从 n
变成 0 的最小期望代价;
状态初始:f(0) = 0
(0已经是目标值,所以代价为0);
- 操作1: r e s 1 = X + f ( ⌊ N A ⌋ ) res_1= X + f(\left \lfloor \frac{N}{A} \right \rfloor) res1=X+f(⌊AN⌋)
- 操作2: r e s 2 = ∑ i = 1 6 ( f ( ⌊ N i ⌋ ) + y ) / 6 res_2=\sum^{6}_{i=1}{(f(\left \lfloor \frac{N}{i} \right \rfloor) + y)} / 6 res2=∑i=16(f(⌊iN⌋)+y)/6
那么 f ( N ) f(N) f(N) 的最小期望代价为 m i n ( r e s 1 , r e s 2 ) min(res_1, res_2) min(res1,res2)
做一下记忆化处理
// 记忆化
static Map<Long, Double> st = new HashMap<>();
static double dfs(long a, long x, long y, long u) {
Double v = st.getOrDefault(u, null);
if (v != null) return v;
if (u == 0) {
return 0.0;
}
// 操作1的结果
double res1 = dfs(a, x, y, u / a) + x;
// 操作二的结果
double res2 = 0;
// 这样写会死循环,因为当i=1时会循环
for (int i = 1; i <= 6; i ++) {
res2 += (dfs(a, x, y, u / i) + y) / 6;
}
// 假设投骰子不会投到1,设置代价为 y * 6/5
res2 += (double) (y * 6) / 5;
for (int i = 2; i <= 6; i ++) {
res2 += dfs(a, x, y, u / i) / 5;
}
double res = Math.min(res1, res2);
st.put(u, res);
return res;
}
static void solve() throws IOException {
String[] ins = pStringArray();
long n = pLong(ins[0]), a = pLong(ins[1]), x = pLong(ins[2]), y = pLong(ins[3]);
out.printf("%.10f", dfs(a, x, y, n));
}
模板
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
public class Example {
static void solve() throws IOException {
}
public static void main(String[] args) throws IOException {
int t = 1;
// t = pInt(in.readLine());
while (t-- > 0) {
solve();
}
out.flush();
out.close();
in.close();
}
static InputStreamReader input = null;
static {
try {
input = new InputStreamReader(Files.newInputStream(Paths.get("D:\\Project\\Problem\\tianti\\src\\in.txt")));
} catch (IOException e) {
input = new InputStreamReader(System.in);
}
}
static final BufferedReader in = new BufferedReader(input);
static final PrintWriter out = new PrintWriter(System.out);
static String[] pStringArr() throws IOException {
return in.readLine().split(" ");
}
static int pInt(String s) {
return Integer.parseInt(s);
}
static long pLong(String s) {
return Long.parseLong(s);
}
static int[] pIntArr(int idx) throws IOException {
String[] ins = pStringArr();
int[] res = new int[ins.length + idx];
for (int i = 0, j = idx; i < ins.length; i++, j++) {
res[i] = Integer.parseInt(ins[j]);
}
return res;
}
static long[] pLongArr(int idx) throws IOException {
String[] ins = pStringArr();
long[] res = new long[ins.length + idx];
for (int i = 0, j = idx; i < ins.length; i++, j++) {
res[i] = Long.parseLong(ins[j]);
}
return res;
}
}