2022 ICPC Shenyang
2022 ICPC Shenyang
C
区间越大越好、答案区间的左右端点一定可以是已有的数,然后就可以 n 方做了
E
不太懂啊,虽然过了
i64 pw2[N * N + 5];
std::vector<int> adj[N + 5];
int d[N + 5], back[N + 5]; // back 表示 i 的子树下有多少点与 i 的祖先有边
bool vis[N + 5];
int sz[N + 5];
i64 f[N + 5][N + 5], g[N + 5];
void add(i64 &a, i64 b) {
a += b;
if (a >= Mod) {
a -= Mod;
}
}
void pre(int cur, int lst) {
d[cur] = d[lst] + 1;
for (int &to : adj[cur]) {
if (to == lst) {
continue;
}
if (d[to] == 0) {
pre(to, cur);
back[cur] += back[to];
}
else if (d[to] < d[cur]) {
back[cur] += 1;
back[to] -= 1;
}
}
}
void dfs(int cur, int lst) {
vis[cur] = true;
sz[cur] = 1;
f[cur][1] = 1;
for (auto &to : adj[cur]) {
if (to == lst || vis[to]) {
continue;
}
dfs(to, cur);
for (int i = 1; i <= sz[cur]; ++i) {
g[i] = f[cur][i];
f[cur][i] = 0;
}
for (int i = 1; i <= sz[cur]; ++i) {
for (int j = 1; j <= sz[to]; ++j) {
// i * j - 1 是两个连通块之间可连的边数
// -1 是因为 cur - to 已经是连起来的了
add(f[cur][i + j], g[i] * f[to][j] % Mod * pw2[i * j - 1] % Mod);
}
}
// 容斥
if (back[to] == 0) {
i64 sum = 0;
for (int i = 1; i <= sz[to]; ++i) {
add(sum, f[to][i]);
}
sum = Mod - sum;
for (int i = 1; i <= sz[cur]; ++i) {
add(f[cur][i], sum * g[i] % Mod);
}
}
sz[cur] += sz[to];
}
}
void solve() {
int n = 0, m = 0;
std::cin >> n >> m;
for (int i = 0; i < m; ++i) {
int u = 0, v = 0;
std::cin >> u >> v;
adj[u].push_back(v);
adj[v].push_back(u);
}
pre(1, 0);
dfs(1, 0);
i64 ans = 0;
for (int i = 1; i <= n; ++i) {
add(ans, f[1][i]);
// debug("%lld ", f[1][i]);
}
i64 inv2 = (Mod + 1) / 2;
// 消除掉非树边的影响
for (int i = 0; i < m - (n - 1); ++i) {
(ans *= inv2) %= Mod;
}
std::cout << ans << '\n';
}
L
直接爆搜即可:
std::array<int, 2> n;
// s[0/1][i] 第 0/1 个阵营里面第 i 个人的状态,0 代表血量,1 代表攻击次数
std::array<int, 2> s[2][N];
int atk[2][N];
// u 代表当前攻击的人
std::array<long double, 3> dfs(int u) {
std::array<long double, 3> res = { 0.0, 0.0, 0.0 };
int v = u ^ 1; // 被攻击的人
std::array<int, 2> cnt = { 0, 0 };
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < n[i]; ++j) {
if (s[i][j][0] > 0) {
cnt[i] += 1;
}
}
}
// 平局
if (!cnt[0] && !cnt[1]) {
res[2] = 1.0;
return res;
}
// 1 胜利
if (!cnt[0]) {
res[1] = 1;
return res;
}
// 0 胜利
if (!cnt[1]) {
res[0] = 1;
return res;
}
// 找 u 中攻击次数的最小值
int mn = Inf;
for (int i = 0; i < n[u]; ++i) {
if (s[u][i][0] > 0) {
mn = std::min(mn, s[u][i][1]);
}
}
for (int i = 0; i < n[u]; ++i) {
if (s[u][i][0] <= 0 || s[u][i][1] > mn) {
continue;
}
// 找第一个活着攻击次数最小的
int hpu = s[u][i][0];
int atku = atk[u][i];
for (int j = 0; j < n[v]; ++j) {
if (s[v][j][0] <= 0) {
continue;
}
int hpv = s[v][j][0];
int atkv = atk[v][j];
s[u][i][0] -= atkv;
s[u][i][1] += 1;
s[v][j][0] -= atku;
auto ret = dfs(v);
for (int k = 0; k < 3; ++k) {
res[k] += ret[k] / cnt[v];
}
s[u][i][0] += atkv;
s[u][i][1] -= 1;
s[v][j][0] += atku;
}
break;
}
return res;
}
std::array<long double, 3> dfs() {
std::array<long double, 3> res = { 0.0, 0.0, 0.0 };
auto ret0 = dfs(0);
auto ret1 = dfs(1);
for (int i = 0; i < 3; ++i) {
res[i] = (ret0[i] + ret1[i]) / 2.0;
}
return res;
}
void solve() {
std::cin >> n[0] >> n[1];
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < n[i]; ++j) {
std::cin >> atk[i][j];
s[i][j] = { atk[i][j], 0 };
}
}
std::cout << std::fixed << std::setprecision(10);
if (n[0] > n[1]) {
for (auto &i : dfs(0)) {
std::cout << i << '\n';
}
}
else if (n[1] > n[0]) {
for (auto &i : dfs(1)) {
std::cout << i << '\n';
}
}
else {
for (auto &i : dfs()) {
std::cout << i << '\n';
}
}
}
浙公网安备 33010602011771号