王负剑,王复健!差不多三个月没打A了,期间除了力扣啥也没写,于是集NTT众人进行复健仪式,复健材料为Baozii Cup 3。本来是想跟邀请赛选拔赛的榜的,误打误撞打成这个了。最后弄出来4题,这比赛还是挺难的感觉。
有一道概率论的题感觉非常典,完了单独总结下 :)
签到。鄙人还是那样非常擅长签到。按二进制考虑,显然某个数取0的时候才能满足题意,两个数从 $0$ 到 $n$ 和从 $0$ 到 $m$ 各取一遍,再减掉同为 $0$ 的情况,答案即为 $n+m+1$ 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <bits/stdc++.h> using namespace std;void solve () { int n, m; cin >> n >> m; cout << n + m + 1 << "\n" ; } signed main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int t = 1 ; cin >> t; while (t--) { solve (); } }
给定可能存在自环和重边的有向图,判断每个点是否在任意奇环上。
另外感觉题意给的奇环定义错得离谱,最后是按样例判断确实是奇环才做下去的。
做法:先SCC缩点,相同分量内部再红蓝染色,如果找到两个颜色相同的点说明分量内部存在奇环,按照题目定义奇环序列的点可以重复,那么总能构造出满足题意的奇环来包含分量内部的任意点,也就是这个分量的所有点都符合题意。
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 #include <bits/stdc++.h> using ll = long long ;using i64 = long long ;using namespace std;void solve () { int n, m; cin >> n >> m; vector<vector<int >> e (n + 1 ); for (int u, v, i = 0 ; i < m; ++i) { cin >> u >> v; e[u].push_back (v); } vector<int > dfn (n + 1 ) , low (n + 1 ) , instk (n + 1 ) , scc (n + 1 ) , sz (n + 1 ) , stk (n + 1 ) ; int tot{}, cnt{}, cntt{}, top{}; auto dfs = [&](this auto && dfs, int u) -> void { dfn[u] = low[u] = ++tot; stk[++top] = u, instk[u] = 1 ; for (int v : e[u]) { if (!dfn[v]) { dfs (v); low[u] = min (low[u], low[v]); } else if (instk[v]) { low[u] = min (low[u], dfn[v]); } } if (dfn[u] == low[u]) { int v; cnt++; do { v = stk[top--]; instk[v] = 0 ; scc[v] = cnt; sz[cnt]++; } while (v != u); if (sz[cnt] > 1 ) cntt++; } }; for (int i=1 ;i<=n;++i) { if (!dfn[i]){ dfs (i); } } vector<int > ans (n+1 , -1 ) , vis (n+1 , -1 ) ; auto bfs = [&](int s) -> int { queue<int > q; q.emplace (s); vis[s] = 0 ; while (!q.empty ()){ int u = q.front (); q.pop (); for (int v:e[u]){ if (scc[u] != scc[v]){ continue ; } if (vis[v] == -1 ){ vis[v] = vis[u]^1 ; q.emplace (v); continue ; } if (vis[v] == vis[u]){ return 1 ; } } } return 0 ; }; for (int i=1 ;i<=n;++i){ if (ans[scc[i]] == -1 ){ ans[scc[i]] = bfs (i); } cout << ans[scc[i]]; } cout << "\n" ; return ; } signed main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int t = 1 ; cin >> t; while (t--) { solve (); } }
被交互打晕了,但是等老许出了做法以后感觉确实很合理也不难理解。。只能说好久没做了脑子有点锈。
按照题意,要找到一个入度为 $n-1$ 且出度为 $0$ 的点,那么这种点最多存在一个。于是维护一个 $cur$ 表示可能是答案的点,初始值为 $1$,然后从 $2$ 到 $n$ 依次往后判断,如果 $cur$ 到 $i$ 存在边,那么 $cur$ 不可能为答案,令 $cur=i$ ,否则 $i$ 不会是答案, $cur$ 不变。这样询问 $n-1$ 次后,能确定唯一一个可能是答案的点,再做 $2n-2$ 次询问即可,总询问次数 $3n-3$ 满足题意。
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 #include <bits/stdc++.h> using ll = long long ;using namespace std;void solve () { int n; cin>>n; auto query=[&](int u,int v){ cout<<"? " <<u<<" " <<v<<endl; int res; cin>>res; return res; }; int cur=1 ; for (int i=2 ;i<=n;++i){ if (query (cur,i)){ cur=i; } } int flag=1 ; for (int i=1 ;i<=n;++i){ if (i==cur) continue ; flag &= !query (cur, i); flag &= query (i, cur); } if (!flag) cur = -1 ; cout<<"! " <<cur<<endl; } signed main () { int t = 1 ; cin >> t ; while (t--) { solve (); } }
什么叫给我MiniC语言解释器让我现场学代码?(x
思洋写的,有空会补。总之是给了个不带控制流和算术操作的语言,只能进行变量自增自减,然后给定一个排列,要找到每个元素之后第一个小于该元素的值对应的下标。
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 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 #include <bits/stdc++.h> std::string program = R"( int n; int p[1010]; int q[1010]; func nop{} func finput[2]; int arrinput[1010]; int iinput; func input{ read p[iinput]; int t; t = p[iinput]; t++; q[t] = iinput; iinput++; call finput[arrinput[iinput]]; } int cur; int ans; int vis[1010]; func fclear[2]; int arrclear[1010]; int iclear; func clear{ vis[iclear] = 0; iclear++; call fclear[arrclear[iclear]]; } func finit[2]; int arrinit[1010]; int iinit; func init{ iinit++; vis[q[iinit]] = 1; call finit[arrinit[iinit]]; } func ffill[2]; int ifill; func fill{ ifill++; int t; t = ifill; call ffill[vis[ifill]]; vis[t] = 1; } func floop[2]; int arrloop[1010]; int iloop; func loop{ ffill[0] = fill; ffill[1] = nop; ifill = q[iloop]; call fill; iloop--; call floop[arrloop[iloop]]; } func ffind[2]; int ifind; func findans{ ans = ifind; } func find{ ifind++; call ffind[vis[ifind]]; } func get{ fclear[1] = nop; fclear[0] = clear; iclear = cur; arrclear[n] = 1; call clear; finit[1] = nop; finit[0] = init; iinit = p[cur]; int t; t = n; t++; arrinit[t] = 1; call init; vis[cur] = 1; vis[n] = 1; floop[1] = nop; floop[0] = loop; iloop = p[cur]; arrloop[0] = 1; call floop[arrloop[iloop]]; vis[n] = 0; ffind[1] = find; ffind[0] = findans; ifind = cur; call find; } func fsolve[2]; int arrsolve[1001]; int isolve; func solve{ cur = isolve; call get; println ans; isolve++; call fsolve[arrsolve[isolve]]; } func main{ read n; finput[1] = nop; finput[0] = input; iinput = 0; arrinput[n] = 1; call input; fsolve[1] = nop; fsolve[0] = solve; isolve = 0; arrsolve[n] = 1; call solve; } )" ;int main () { std::cout << program << "\n" ; }
重头戏。场上没写出来,但是感觉非常典于是一定要补。
给定 $n\times m$ 的网格,进行 $k$ 次操作,每次随机选择一个格子,若为白则染黑。问最后得到的黑色联通块的周长和的期望。
直接做很难,我们分解成每个黑色格子对答案的贡献。一个黑格子会贡献 $4$ 的周长,但同时两个相邻的黑格子会相互抵消 $2$ 的周长。因此周长 $P = 4X - 2A$ ,其中 $X$ 是黑格子数量,$A$ 是相邻黑格子对数。
根据期望的线性性,我们得到 $E(P) = 4E(X)-2E(A)$ ,我们分成两部分来求。
第一部分求 $E(X)$ ,同样地,直接整体递推太难了,我们考虑每个格子的贡献。一个格子被染成黑色的概率 $P(u 黑) = 1-P(u白)=1-(\frac{nm-1}{nm})^k$ 。那么 $E(X)=nmP(u黑)$ 。
第二部分求 $E(A)$ ,依旧考虑每对格子同时为黑的概率。$P(u黑v黑)=1-P(u白)-P(v白)+P(u白v白)=1-2(\frac{nm-1}{nm})^k+(\frac{nm-2}{nm})^k$ 。那么 $E(A) = \left(n(m-1)+m(n-1)\right)P(u黑v黑)$。
整理得到最终答案 $E(P)=2(n+m)+4(nm-n-m)(\frac{nm-1}{nm})^k-2(2nm-n-m)(\frac{nm-2}{nm})^k$ 。
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 #include <bits/stdc++.h> using namespace std;using i64 = long long ;constexpr int N = 2e5 + 10 ;constexpr int MOD = 1'000'000'007 ;i64 ksm (i64 a, i64 b) { i64 res{1 }; a %= MOD; while (b) { if (b & 1 ) res = res * a % MOD; a = a * a % MOD; b >>= 1 ; } return res; } void solve () { i64 n, m, k; cin >> n >> m >> k; i64 inv = ksm (n * m, MOD - 2 ); i64 ans = 2 * (n + m) % MOD; ans = (ans + 4 * (n * m % MOD - n - m) % MOD * ksm ((n * m - 1 ) % MOD * inv, k)) % MOD; ans = (ans - 2 * (2 * n * m % MOD - n - m) % MOD * ksm ((n * m - 2 ) % MOD * inv, k)) % MOD; ans = (ans + MOD) % MOD; cout << ans << "\n" ; } signed main () { ios::sync_with_stdio (false ); cin.tie (nullptr ); int t = 1 ; cin >> t; while (t--) { solve (); } return 0 ; }