C. Black Intervals
考虑翻转前后对答案有什么影响
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, q;
cin >> n >> q;
vector<int> c(n+2);
int ans = 0;
auto add = [&](int i, int x) {
if (c[i] == 0 and c[i+1] == 1) ans += x;
};
rep(qi, q) {
int a;
cin >> a;
add(a-1, -1);
add(a, -1);
c[a] ^= 1;
add(a-1, 1);
add(a, 1);
cout << ans << '\n';
}
return 0;
}
D. Conflict 2
从后往前处理操作,追踪服务器的最终字符串是由哪个 \(PC\) 的字符串决定的(通过 \(pc\) 变量),并且只记录那些在形成这个 \(PC\) 字符串的过程中追加的操作 \(2\)。同时,当我们遇到操作 \(1\)(将PC p覆盖为服务器的字符串)时,如果这个 \(p\) 正好是我们正在追踪的\(PC\),那么我们就不能继续追踪这个 \(PC\) 了(因为它的内容被服务器覆盖了,而服务器当时的内容我们还没有确定来源?实际上,在反向处理中,我们还没有处理到更早的操作,所以这个覆盖操作会打断我们当前追踪的链,因此我们将 \(pc\) 置 \(0\),表示这个 \(PC\) 之后的操作不再影响最终结果)。
服务器的最终字符串是由最后一次操作 \(3\) 决定的(或者如果没有操作 \(3\) 则为空)。然后,我们反向追踪这个 \(PC\) 的字符串是如何形成的。在反向处理中,当我们遇到一个操作 \(3\)(将服务器设为某个 \(PC\) 的字符串)时,如果此时 \(pc\) 为 \(0\)(表示还没有确定来源),我们就设置 \(pc\) 为这个 \(PC\),表示我们要追踪这个 \(PC\)。然后,在追踪过程中,我们只记录那些对这个 \(PC\) 的追加操作 \(2\),并且当遇到一个覆盖操作 \(1\) 将这个 \(PC\) 的内容覆盖为服务器的字符串时,我们就停止追踪(因为此时这个 \(PC\) 的内容不再由之前的追加操作决定,而是由当时服务器的内容决定,而当时服务器的内容我们还没有反向追踪到,所以需要中断)。
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
int main() {
int n, q;
cin >> n >> q;
vector<tuple<int, int, string>> qs;
rep(qi, q) {
int type, p; string s;
cin >> type >> p;
if (type == 2) cin >> s;
qs.emplace_back(type, p, s);
}
int pc = 0;
vector<string> adds;
ranges::reverse(qs);
for (auto [type, p, s] : qs) {
if (type == 1) {
if (p == pc) pc = 0;
}
else if (type == 2) {
if (pc == p) adds.push_back(s);
}
else {
if (pc == 0) pc = p;
}
}
string ans;
ranges::reverse(adds);
for (string s : adds) ans += s;
cout << ans << '\n';
return 0;
}
E. E [max]
贡献法
\(E[\max] = \sum\limits_{x=1}^{10^9} P[\max \geqslant x] = \sum\limits_{x=1}^{10^9} (1 - P[\max < x])\) (期望的积分形式)
发现外面有 \(10^9\) 显然超时,事实上,我们不需要用到这么多数,而只需要用到 \(a\) 中的数,可以将 \(a\) 排序后再按顺序遍历 \(a\) 中的每个值 \(x\),然后利用连续性计算差值,那么 \(x\) 对答案的贡献就是这个概率乘以个数 \(x-pre\)
这样一来,外层循环就退化到了 \(6N\)
但这样还是超时
可以考虑动态维护乘积
记 cnt[i] 表示每个骰子已经处理过的面数,prod 表示之前已经处理过的 \(cnt[i]\) 的乘积,zero 表示尚未处理的骰子数
代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
using mint = modint998244353;
int main() {
int n;
cin >> n;
int m = 6;
vector a(n, vector<int>(m));
rep(i, n)rep(j, m) cin >> a[i][j];
vector<P> xs;
rep(i, n)rep(j, m) xs.emplace_back(a[i][j], i);
sort(xs.begin(), xs.end());
mint ans;
int pre = 0;
mint prod = 1; int zero = n;
vector<int> cnt(n);
for (auto [x, i] : xs) {
// mint p = 1;
// rep(i, n) {
// int cnt = 0;
// rep(j, m) if (a[i][j] < x) cnt++;
// p *= cnt;
// }
// p /= mint(m).pow(n);
mint p;
if (zero == 0) p = prod / mint(m).pow(n);
ans += (mint(1)-p) * (x-pre);
pre = x;
if (cnt[i] == 0) zero--; else prod /= cnt[i];
cnt[i]++;
prod *= cnt[i];
}
cout << ans.val() << '\n';
return 0;
}
F. Contraction
启发式合并
当合并顶点时,遵循以下原则:
- 始终将“棋子数量+邻边数”较少的一方合并到较多的一方
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
int main() {
int n, m;
cin >> n >> m;
vector<P> es;
vector<set<int>> to(n);
rep(i, m) {
int a, b;
cin >> a >> b;
--a; --b;
es.emplace_back(a, b);
to[a].insert(b);
to[b].insert(a);
}
vector<int> vid(n);
vector<vector<int>> vs(n);
rep(i, n) vid[i] = i, vs[i].push_back(i);
int q;
cin >> q;
int ans = m;
rep(qi, q) {
int ei;
cin >> ei;
--ei;
auto [a, b] = es[ei];
a = vid[a]; b = vid[b];
if (a != b) {
if (vs[a].size() > vs[b].size()) swap(a, b);
to[a].erase(b);
to[b].erase(a);
ans--;
for (int c : to[a]) {
if (to[b].count(c)) ans--;
to[c].erase(a);
to[c].insert(b);
}
to[b].merge(to[a]);
to[a].clear();
for (int v : vs[a]) {
vid[v] = b;
vs[b].push_back(v);
}
vs[a].clear();
}
cout << ans << '\n';
}
return 0;
}
G. Count Cycles
状压dp
考虑删除环里编号最大顶点的一条相邻边,破环城链
记 dp[S][v] 表示路径上包含的点集为 \(S\) 且路径非最大顶点侧的端点为 \(v\) 的路径数
时间复杂度为 \(O(2^NN^2)\)
注意最后还需对答案除以 \(2\)
代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using mint = modint998244353;
int main() {
int n, m;
cin >> n >> m;
vector g(n, vector<int>(n));
rep(i, m) {
int a, b;
cin >> a >> b;
--a; --b;
g[a][b]++; g[b][a]++;
}
mint ans;
rep(x, n) {
int x2 = 1<<x;
vector dp(x2, vector<mint>(x));
rep(v, x) dp[1<<v][v] = g[x][v];
rep(s, x2)rep(v, x) if (s>>v&1) {
rep(u, x) if (~s>>u&1) {
dp[s|1<<u][u]+=dp[s][v]*g[v][u];
}
}
rep(s, x2)rep(v, x) if (s>>v&1) {
if (s == (1<<v)) continue;
mint now = dp[s][v]*g[v][x];
ans += now;
}
rep(v, x) {
ans += mint(g[v][x]-1)*g[v][x];
}
}
ans /= 2;
cout << ans.val() << '\n';
return 0;
}
浙公网安备 33010602011771号