比赛链接:
https://ac.nowcoder.com/acm/contest/33195
F.Shannon Switching Game?
题意:
给定一个无向图,初始有一张纸牌在点 \(s\),两个玩家 \(Join Player\) 和 \(Cut Palyer\) 轮流行动,\(Cut Palyer\) 先动。
\(Cut Palyer\) 每次可以移除一条和纸牌所在点相邻的边,\(Join Player\) 每次可以让纸牌沿着当前点的一条未删除的边移动。
如果纸牌某时刻被移动到 \(t\) 则 \(Join Player\) 获胜,否则 \(Cut Palyer\) 获胜,问双方均采用最优策略的情况下,谁会获胜。
思路:
从终点出发来考虑,将满足条件的点作为一个集合,某个点要有两条能到达集合的点中的路径才能够到达这个终点所包含的集合,因为 \(Cut Player\) 删掉一条边之后,还能够过来,按照这个思路进行一个 \(bfs\) 就行。
题意:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
LL n, m, s, t;
cin >> n >> m >> s >> t;
vector < vector<LL> > e(n + 1);
for (int i = 0; i < m; i ++ ){
LL u, v;
cin >> u >> v;
e[u].push_back(v);
e[v].push_back(u);
}
auto bfs = [&](){
vector <LL> deg(n + 1), used(n + 1);
used[t] = 1;
queue <LL> q;
q.push(t);
while(q.size()){
LL u = q.front();
q.pop();
for (auto v : e[u]){
if (used[v]) continue;
deg[v] ++ ;
if (deg[v] >= 2){
used[v] = 1;
q.push(v);
}
}
}
if (used[s]) cout << "Join Player\n";
else cout << "Cut Player\n";
};
bfs();
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL T = 1;
cin >> T;
while(T -- ){
solve();
}
return 0;
}
H.Wheel of Fortune
题意:
两个人玩炉石,两个人的血量分别为 \(A\) 和 \(B\),每人有七个兵,血量分别为 \(a_i\) 和 \(b_i(1 <= i <= 7)\),每回合随机选择一个单位扣 10 点血,问 \(A\) 获胜的概率,答案对 998244353 取模。
思路:
因为只角色死亡才会结束游戏,所以兵的血量其实没有影响。先计算出每个人可以承受多少次伤害,即 \(\lceil \frac{A}{10} \rceil\) 和 \(\lceil \frac{B}{10} \rceil\),分别记为 \(a\) 和 \(b\)。
接下来就枚举每一种可能,\(A\) 最多能承受 \(a - 1\) 次伤害(因为要赢,所以还要有血)。
承受 0 次伤害,答案就是 \(\frac{1}{2^b}\)。
承受 1 次伤害,只要在 \(b\) 死之前受一次伤害就行,考虑插空法,这一次伤害有 \(C_{b}^{1}\) 种可能,答案就是 \(C_{b}^{1} \frac{1}{2^{b + 1}}\)。
承受 2 次伤害,\(C_{b + 1}^{2} \frac{1}{2^{b + 2}}\)
...
通过递推求解就行。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int P = 998244353;
LL qp(LL a, LL k, LL p){
LL ans = 1;
while (k){
if (k & 1) ans = ans * a % p;
k >>= 1;
a = a * a % p;
}
return ans;
}
int main(){
ios::sync_with_stdio(false);cin.tie(0);
LL A, B, t;
cin >> A;
for (int i = 1; i <= 7; i ++ )
cin >> t;
cin >> B;
for (int i = 1; i <= 7; i ++ )
cin >> t;
LL a = (A + 9) / 10, b = (B + 9) / 10;
LL x = 1, y = qp(2, b, P), ans = qp(y, P - 2, P);
for (int i = 1; i < a; i ++ ){
x = x * (b + i - 1) % P * qp(i, P - 2, P) % P;
y = y * 2 % P;
ans = (ans + x * qp(y, P - 2, P) % P) % P;
}
cout << ans << "\n";
return 0;
}
I.Yet Another FFT Problem?
题意:
给定两个序列 \(a\) 和 \(b\),判断存不存在 \(\lvert a_i - a_j \rvert = \lvert b_k - b_l \rvert\)(\(i != j, k != l\)),若存在输出 \(i, j, k, l\),否则输出 -1。
思路:
转化一下等式,变成 \(a_i + b_l = a_j + b_k\),因为两个序列中的最大值之和只有 \(2e7\),所以可以暴力枚举两个数之和。
还要考虑一个特殊情况,\(a_i = a_j, b_k = b_l\)。
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10;
int main(){
ios::sync_with_stdio(false);cin.tie(0);
int n, m;
cin >> n >> m;
vector <int> a(n + 1), b(m + 1);
for (int i = 1; i <= n; i ++ )
cin >> a[i];
for (int i = 1; i <= m; i ++ )
cin >> b[i];
vector <int> fa(N), fb(N), ans(4), A, B;
for (int i = 1; i <= n; i ++ ){
if (fa[a[i]] == 0){
fa[a[i]] = i;
A.push_back(i);
}
else{
ans[0] = fa[a[i]];
ans[1] = i;
}
}
for (int i = 1; i <= m; i ++ ){
if (fb[b[i]] == 0){
fb[b[i]] = i;
B.push_back(i);
}
else{
ans[2] = fb[b[i]];
ans[3] = i;
}
}
if (ans[0] && ans[2]){ //存在差为 0 的情况
for (int i = 0; i < 4; i ++ )
cout << ans[i] << " \n"[i == 3];
return 0;
}
vector < array<int, 2> > f(N * 2);
for (auto i : A){
for (auto j : B){
if (f[a[i] + b[j]][0] == 0){
f[a[i] + b[j]] = {i, j};
}
else{
cout << i << " " << f[a[i] + b[j]][0] << " " << j << " " << f[a[i] + b[j]][1] << "\n";
return 0;
}
}
}
cout << "-1\n";
return 0;
}