H. Genshin Impact’s Fault(模拟) 时间复杂度:$O(|S|)$ 题意 有四种许愿结果:3星武器,4星武器,5星非促销武器和5星促销武器,分别用字符$’3’、’4’、’5’$和$’U’$表示,给定愿望清单,必须满足以下规则:
对于每个连续的10个愿望,不能只存在3星武器
对于每个连续的90个愿望,至少存在一件5星武器,不管是否促销
对于每两个相邻的5星武器之中,必须有一个是促销武器
给定愿望清单的字符串,请问是否符合所有规则?若满足,输出”$valid$”,反之则输出”$invalid$”.
数据范围
$c \in [‘3’,’4’,’5’,’U’]$
$\sum |S| \le 10^6$
思路 设字符串长度为$n$,对三个规则进行转化:
当$n \ge 10$时,每个长度为10的连续区间是否满足不止存在字符$’3’$
所有$’5’$或$’U’$的间隔距离,必不超过90,记得特判开头第一个和最后一个
相邻的两个五星武器,不能出现连续的两个$’5’$
Code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #include <bits/stdc++.h> #define endl '\n' using i64 = long long; using namespace std; signed main(){ std::ios::sync_with_stdio(false); cin.tie(0); int t; cin >> t; while (t--){ string str;cin >> str; int n = str.length(); str = " " + str; vector<int> pre(n + 1); vector<int> v; bool ok = 1; for (int i = 1; i <= n;i++){ pre[i] = pre[i - 1]; if(str[i]=='3') pre[i] += 3; else if(str[i]=='4') pre[i] += 4; else if(str[i]=='5') pre[i] += 5, v.emplace_back(i); else pre[i] += 6, v.emplace_back(i); } //愿望1 if(n>=10){ for (int i = 10; i <= n;i++) if(pre[i]-pre[i-10]==30) //说明出现了连续的10个'3' ok = 0; } //愿望2 int cur = 0; for(auto it:v){ if(it-cur>90) ok = 0; cur = it; } if(cur+90-1<n) ok = 0; //愿望3 if(v.size()>=2){ for (int i = 1; i < v.size();i++) if(str[v[i]]==str[v[i-1]]&&str[v[i]]=='5') ok = 0; } cout << (ok ? "valid" : "invalid") << endl; } return 0; }
B.Cake2 时间复杂度:$O(1)$ 题意 蛋糕的形状是一个正多边形,边数为$n$,现在按照逆时针顺序标记顶点为0至$n-1$,选择一个正整数$k$,依次连接顶点$i$和顶点$(i+k) \: mod \: n$来切蛋糕,请问蛋糕最终切完的块数是多少?
数据范围
$4 \le n \le 10^6$
$2 \le k \le n-2$
思路 打表找规律,不难发现,当$2 \times k == n$时,此时所有连接线都是正对角线,分割的块数一定是$n$;当$k < \lfloor \frac{n}{2} \rfloor$时,块数等于$n \cdot k+1$,当$k > \lfloor \frac{n}{2} \rfloor$时,与前者答案恰好对称.
Code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include<bits/stdc++.h> #define endl '\n' using i64 = long long; using namespace std; signed main(){ std::ios::sync_with_stdio(false);cin.tie(0); int n, k; cin >> n >> k; i64 ans; if(k*2==n) ans = n; else{ if (k > n / 2) k = n - k; ans = 1LL * n * k + 1; } cout << ans << endl; return 0; }
A. Cake(博弈/树形dp) 时间复杂度:$O(n)$ 题意 Grammy和Oscar在一棵树上进行操作,树上有 $n$ 个点,每两个点之间的边的边权为0或1。起始以 $1$ 作为树的根,字符串 $S$ 是空串,每移动到一个点,两点间的边权将加到字符串 $S$ 的末尾。Grammy作为先手,两人交替移动,直到移动到叶子节点,操作结束。此时将长度为 $m$ 的字符串 $S$ 分成 $m$ 块蛋糕,由Oscar进行切蛋糕操作,取其前缀,然后Grammy取 $1$ 的,Oscar取 $0$ ,两者都会以最优的策略最大化分到的蛋糕数,求最终Grammy获得的最大蛋糕数量( $1$ )的比例 .
数据范围
$2 \le n \le 200000$
$1 \le x,y \le n,0 \le k \le 1$
$\sum n \le 500000$
思路 首先,两者从根节点交替进行移动,最终一定会移动到叶子节点,且每个叶子节点的字符串是固定的,也就是在该点Oscar的分割答案也是固定的,所以每个点的后继节点的答案一定是可预见的,考虑使用叶子到根的树形dp,使用递归求解 .
不难想到,每个点由先手还是后手操作,只与该点的深度有关,即每一点的操作对象已知。对于先手Grammy而言,它期望的是后继节点中 $1$ 的比例更大,后手则反之;设当前结点 $u$ 的前缀 $0$ 的最大比例为 $dp[u]$ , 显然,当Grammy操作时,对后继节点取$max$ ,当Oscar操作时,对后继节点取 $min$ 即可 ,同时对叶子节点进行特判 .
对于叶子节点中求解前缀 $0$ 的比例最大值,注意到每一次向下深搜时匹配到的就是前缀,所以直接设一个数组维护当前字符串中 $0$ 的数量和字符串长度,维护最大值即可 .
Code: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include<bits/stdc++.h> #define endl '\n' using i64 = long long; using namespace std; void solve(){ int n; cin >> n; vector<vector<pair<int,int>>> e(n + 1); vector<double> rt(n + 1), mx(n + 1); // rt[i]表示i号点前缀0的占比,mx[u]表示到达u节点的前缀0比例最大值 for (int i = 1; i < n;i++){ int x, y, k; cin >> x >> y >> k; e[x].emplace_back(y, k); e[y].emplace_back(x, k); } auto dfs = [&](auto dfs, int u, int fa, int depth, int cnt) -> double { if(u!=1) mx[u] = max(mx[fa], (cnt * 1.0) / (depth - 1)); //维护前缀0比例最大值 //cout << u << ' ' << depth << ' ' << cnt << ' ' << mx[u] << endl; if (fa != 0 && e[u].size() == 1) // 叶子节点 rt[u] = mx[u]; else{ rt[u] = ((depth & 1) ? 1.0 : 0.0); for (auto x:e[u]){ if(x.first==fa) continue; if (depth & 1) // 轮到Grammy rt[u] = min(rt[u], dfs(dfs, x.first, u, depth + 1, cnt + (!x.second))); else //轮到Oscar rt[u] = max(rt[u], dfs(dfs, x.first, u, depth + 1, cnt + (!x.second))); } } return rt[u]; }; double ans = 1.0 - dfs(dfs, 1, 0, 1, 0); //前缀1的比例 cout << fixed << setprecision(12) << ans << endl; } signed main(){ std::ios::sync_with_stdio(false);cin.tie(0); int t; cin >> t; while(t--) solve(); return 0; }
典题 https://codeforces.com/contest/1970/problem/C2