2023河南萌新赛第五场vp记录
solved 8 problems
A、B、E、G: @dbywsc
I、J、L: @xmjlove
D:@DASJ
A.买爱心气球
思路
对于小气球,可以直接当作普通的 Nim 游戏处理,非 \(0\) 即有必胜态;对于大气球而言,打表观察可以发现,当大气球的数量为 \(3\) 的倍数或者 \(0\) 时,Alice 必败,因此必胜态为 \(m \mod 3 \neq 0\) 。
所以可以当作变形的 Nim 游戏。 Alice 可以获胜当且仅当 \((m \mod 3) \oplus n \neq 0\) 。
代码
void solve(void) {
int m, n; std::cin >> m >> n;
m %= 3;
std::cout << ((m ^ n) ? "Alice" : "Bob") << '\n';
}
B.亚托莉 -我挚爱的时光-
思路
这个没什么好说的,模拟。
代码
void solve() {
int n;
std::cin >> n;
std::cin.ignore();
std::map<std::string, int> pac;
for (int i = 0; i < n; i++) {
std::string s;
std::getline(std::cin, s);
if (s == "sudo rm -rf /*") {
std::cout << "wuwuwu\n";
return;
}
if (s.find("sudo pacman -S ", 0) == 0) {
std::string name = s.substr(15);
pac[name] = 1;
}
else if(s.rfind("pacman -Rscn ", 0) == 0) {
std::string name = s.substr(13);
pac[name] = -1;
}
else if(s.rfind("pacman -R ", 0) == 0) {
std::string name = s.substr(10);
pac[name] = 0;
}
else if(s[0] == '1') {
std::string name = s.substr(2);
if(pac.count(name) && pac[name] == 1) {
std::cout << "yes\n";
} else std::cout << "no\n";
}
else if(s[0] == '2') {
std::string name = s.substr(2);
if(pac.count(name) && pac[name] != -1) {
std::cout << "yes\n";
} else std::cout << "no\n";
}
}
}
D.01分数规划
思路
复制两个字符串,一个将问号全部换成1,另一个将问号全部换成0,然后开始遍历这两个字符串,找出最长的1或者0。
代码
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n;
cin>>n;
string s;
cin>>s;
string s1=s;
string s2=s;
for(int i=0;i<n;i++){
if(s1[i]=='?'){
s1[i]='0';
}
}
for(int i=0;i<n;i++){
if(s2[i]=='?'){
s2[i]='1';
}
}
int ans=0;
int yi=0;
int ling=0;
for(int i=0;i<n;i++){
if(s1[i]=='1'){
ans=max(ans,ling);
ling=0;
yi++;
}else if(s1[i]=='0'){
ans=max(ans,yi);
yi=0;
ling++;
}
}
int shu=max(yi,ling);
ans=max(ans,shu);
yi=0;
ling=0;
for(int i=0;i<n;i++){
if(s2[i]=='1'){
ans=max(ans,ling);
ling=0;
yi++;
}else if(s2[i]=='0'){
ans=max(ans,yi);
yi=0;
ling++;
}
}
shu=max(yi,ling);
ans=max(ans,shu);
cout<<ans<<endl;
}
return 0;
}
E.换根DP
思路
由于边权一定是 \(1\) 或 \(2\) ,因此两点距离为 \(2\) 当且仅当路径之间的不存在权为 \(1\) 的边。因此可以删掉边权为 \(1\) 的边,此时树就变成了多个连通块。显然 \(min(f(i))\) 存在于最小的连通块中。
代码
struct DSU {
std::vector<int> p, siz;
DSU(int n): p(n + 1), siz(n + 1, 1) {
std::iota(p.begin(), p.end(), 0);
}
int find(int x) {
if(x == p[x]) return p[x];
return p[x] = find(p[x]);
}
void unite(int a, int b) {
int pa = find(a), pb = find(b);
if(pa == pb) return;
if(siz[pa] < siz[pb]) std::swap(pa, pb);
p[pb] = pa;
siz[pa] += siz[pb];
}
bool same(int u, int v) {return find(u) == find(v);}
int size(int u) {return siz[find(u)];}
};
struct Edge {
int u, v, w;
};
void solve() {
int n; std::cin >> n;
DSU dsu(n);
for(int i = 1; i < n; i++) {
int u, v, w; std::cin >> u >> v >> w;
if(w == 1) continue;
dsu.unite(u, v);
}
int minn = 1e9;
for(int i = 1; i <= n; i++) {
minn = std::min(minn, dsu.size(i));
}
i64 ans = 2 * (minn - 1) + (n - minn);
std::cout << ans << '\n';
}
G.Kruskal
思路
由于是完全图,因此生成树只要在完全图中找出不构成环且联通的 \(n - 1\) 条边就行。因为求最小生成树,所以考虑下面的构造:
-
以 \(1\) 为根。并且预处理出 \(n\) 以内的 \(2^i\) 。
-
对于任意偶数,连接到根结点上,代价为 \(0\) ;
-
对于范围内的 \(2^i - 1\) 号节点,连接到 \(2^i\) 号节点上,代价也为 \(0\) ;
-
对于范围内任意奇数节点,一定存在一个偶数节点,使得两者连接代价为 \(0\) 。
-
此时只剩下两种情况,第一张情况是已经构成了生成树,总代价为 \(0\) ;第二种情况是 \(n\) 号节点刚好是一个 \(2^i - 1\) 形式,由于没有大于它的 \(2^i\) 节点和它匹配,因此它只能连接到根上,代价为 \(1\)。
因此,本题的答案只会是 \(0\) 或者 \(1\) 。
代码
int p[32]; //预处理的 2^i - 1
void solve() {
int n; std::cin >> n;
int cnt = 0;
for(int i = 2; i <= 30; i++) {
if(n == p[i]) cnt++;
}
std::cout << cnt << '\n';
}
I.双指针
思路
将式子变形:
然后直接找就好了。需要注意精度。
代码
public static void main(String[] args) throws Exception {
int t = read();
while (t-- > 0) {
int n = read();
//int n = (int) (Math.random() * 20);
int[] a = new int[n];
int[] b = new int[n];
TreeMap<Double, Long> d1 = new TreeMap<>();
TreeMap<Double, Long> d2 = new TreeMap<>();
for (int i = 0; i < n; i++) {
a[i] = read();
//a[i] = (int) (Math.random() * 20);
}
for (int i = 0; i < n; i++) {
b[i] = read();
//b[i] = (int) (Math.random() * 20);
d1.put((double) a[i] / b[i], d1.getOrDefault((double) a[i] / b[i], 0L) + 1);
d2.put((double) b[i] / a[i], d2.getOrDefault((double) b[i] / a[i], 0L) + 1);
}
long ans = 0;
HashSet<Double> s = new HashSet<>();
for (double val : d1.keySet()) {
if (val > 1) {
continue;
}
if (d2.containsKey(val)) {
if (val == 1.0) {
ans += d1.get(val) * (d1.get(val) - 1) / 2;
} else {
ans += d1.get(val) * d2.get(val);
}
//pw.println(val + " " + d1.get(val) + " " + d1.get(val));
s.add(val);
}
}
pw.println(ans);
}
pw.flush();
}}
pw.flush();
}
J.树上DP
思路
考虑节点对答案的贡献
包含该点的子树越多,贡献越大
dfs处理所有节点的深度,越深的节点分配越高的权值,一个节点的贡献为 深度 * 权值
代码
public static void main(String[] args) throws Exception {
int t = read();
while (t-- > 0) {
int n = read();
map.clear();
for (int i = 0; i <= n; i++) {
map.add(new ArrayList<>());
}
h = new int[n];
for (int i = 1; i < n; i++) {
int u = read();
int v = read();
map.get(u).add(v);
map.get(v).add(u);
}
int[] a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = read();
}
dfs(1, -1, 1);
Arrays.sort(a);
Arrays.sort(h);
long ans = 0;
for (int i = n - 1; i >= 0; i--) {
ans += (long) a[i] * h[i];
}
pw.println(ans);
}
pw.flush();
}
public static ArrayList<ArrayList<Integer>> map = new ArrayList<>();
public static int[] h;
public static void dfs (int root, int father, int hei) {
h[root - 1] = hei;
for (int node : map.get(root)) {
if (node == father) {
continue;
}
dfs(node, root, hei + 1);
}
}
L.异或与
思路
打表观察规律
发现每一个数最多只有一个数与其对应
\(9\) 的二进制为 \(1001\) ,\(27\) 的二进制为 \(11011\),相与等于 \(1001\),异或等于 \(10010\)
注意到首先两个数末位异或必定等于 \(0\)
设当前数二进制为 \(a\) ,与其对应的数二进制为 \(b\) , \(b\) 的第 \(1\) 位已知
有 \(b[i + 1] = ( b[i - 1] \ \& \ a[i - 1] ) \oplus a[i + 1]\)
判断 \(b\) 是否存在于数组即可
代码
int n = Integer.parseInt(br.readLine());
long[] arr = new long[n];
HashMap<Long, Integer> d = new HashMap<>();
String[] input = br.readLine().split(" ");
for (int i = 0; i < n; i++) {
arr[i] = Long.parseLong(input[i]);
d.put(arr[i], i);
}
int flag = 0;
for (int i = 0; i < n; i++) {
long num = arr[i];
long last = num % 2;
num /= 2;
long nnum = last == 1 ? 1 : 0;
long ncur = last == 1 ? 1 : 0;
int cnt = 1;
while (num != 0 || ncur != 0) {
long cur = num % 2;
num /= 2;
ncur = (last & ncur) ^ cur;
nnum += ncur * (long) Math.pow(2, cnt);
last = cur;
cnt++;
}
if (d.containsKey(nnum)) {
if (d.get(nnum) != i) {
flag = 1;
pw.println((i + 1) + " " + (d.get(nnum) + 1));
break;
}
}
}
if (flag == 0) {
pw.println(-1);
}

浙公网安备 33010602011771号