D - Factorial and Multiple
对 \(k\) 进行质因数分解。
如果 \(k\) 最大的质因子 \(p\) 满足 \(p * p > k\) ,那么答案就是 \(p\)。因为一定要包含一次,也只需要包含一次。
否则直接爆搜。
AC代码
// #define MULTIPLE_TASK
#include "hira/main.cpp"
#include "hira/math/prime/pollard_rho.h"
void Initialize() {}
void SolveCase(int Case) {
i64 k;
std::cin >> k;
auto p = math::Factor(k);
logd(p);
if (p.back() > 10'000'000) {
std::cout << p.back() << "\n";
} else {
for (int i = 1;; ++i) {
int g = std::gcd(i, k);
k /= g;
if (k == 1) {
std::cout << i << "\n";
break;
}
}
}
}
E - Critical Hit
枚举用了 \(i\) 个 \(-1\),那么用了多少个 \(-2\) 也就能算出来了,记用了 \(j\) 个 \(-2\) 。
然后,如果 \(i + 2j > n\) ,说明最后一个操作为 \(2\) ,否则最后一个操作任意。
然后就是算一下概率,然后套期望公式了。
递推的做法:\(f(0) = 0, f(1) = 1, f(i) = pf(i - 2) + qf(i-1) + 1\)。
AC代码
// #define MULTIPLE_TASK
#include "hira/main.cpp"
#include "hira/math/modular/binom.h"
#include "hira/math/modular/mod_int.h"
using mint = math::ModInt998;
const auto binom = math::binom<mint>;
void Initialize() {}
void SolveCase(int Case) {
int n, v;
std::cin >> n >> v;
mint p = mint(100 - v) / 100, q = mint(v) / 100;
mint ans = 0;
for (int i = 0; i <= n; ++i) {
int j = (n - i + 1) / 2;
if (i + 2 * j > n)
ans += mint(i + j) * p.power(i) * q.power(j) * binom(i + j - 1, i);
else
ans += mint(i + j) * p.power(i) * q.power(j) * binom(i + j, i);
logd(i, j, ans);
}
std::cout << ans.value() << "\n";
}
F - Pay or Receive
如果不属于同一连通块,就是 nan
,这个可以用并查集搞。
否则,如果该连通块有权非零的环,则 inf
,正环就不解释了,在本题中负环反着跑就是正环。这个的判断可以跑遍 DFS。
现在需要回答多源多汇最长路的询问,普适的算法必定超时,所以要根据题目条件进一步优化。
现在没有权非零的环了,任意两点间的距离是唯一的,因为如果不唯一就能推出有权非零的环。
所以可以转成树上的两点间距离询问,经典。
还可以进一步优化:对于任意两点,\(d(x, y) = -d(y,x)\) ,假设以 \(r\) 为根跑出 DFS 树,那么 \(d(r, x) + d(x, y) + d(y, r) = 0, d(x, y) = -d(r, x) - d(y, r) = d(r, y) - d(r, x)\) 。
AC代码
// #define MULTIPLE_TASK
#include "hira/main.cpp"
#include "hira/ds/dsu.h"
void Initialize() {}
void SolveCase(int Case) {
int n, m, q;
std::cin >> n >> m >> q;
ds::DSU d(n);
std::vector<std::vector<std::pair<i64, int>>> g(n);
for (int i = 0; i < m; ++i) {
int a, b, c;
std::cin >> a >> b >> c;
--a, --b;
d.merge(a, b);
g[a].push_back({c, b});
g[b].push_back({-c, a});
}
const i64 INF = INT64_C(0x3f3f3f3f3f3f3f3f);
std::vector<i64> dist(n, -INF);
std::vector<bool> has_positive_loop(n, false);
for (int i = 0; i < n; ++i) {
if (d.leader(i) != i)
continue;
std::queue<int> q;
q.push(i);
dist[i] = 0;
while (!q.empty()) {
int u = q.front();
q.pop();
for (auto [w, v] : g[u]) {
if (dist[v] == -INF) {
dist[v] = dist[u] + w;
q.push(v);
} else {
if (dist[v] != dist[u] + w)
has_positive_loop[i] = true;
}
}
}
}
for (int i = 0; i < q; ++i) {
int a, b;
std::cin >> a >> b;
--a, --b;
if (!d.same(a, b)) {
std::cout << "nan\n";
continue;
}
if (has_positive_loop[d.leader(a)]) {
std::cout << "inf\n";
continue;
}
std::cout << -dist[a] + dist[b] << "\n";
}
}
G - Do Use Hexagon Grid 2
在这种六角网格上,从 \((0, 0)\) 走到 \((x, y)\) 的距离为 \(\max(|x|, |y|, |x - y|)\)。
证明
感性的理解,六角网格是在四角网格的基础上,新增了右上和左下两个方向的移动。
对于右上方向和左下方向,横纵座标可以同时改变,其中一个到达目标值之后再仅改变另外一个。由此距离为 \(\max(|x|, |y|)\)。
对于左上和右下,只能先改一个座标,再改另一个座标,由此距离为 \(|x| + |y| = |x - y|\)。
综合一下就是距离为 \(\max(|x|, |y|, |x - y|)\)。
然后就可以把六角网格座标转换成三维座标,把距离看成三维座标系中的 Chebyshev 距离。
现在问题转换成有多少个点集满足点集中的点位于某一个 \(d \times d \times d\) 的正方体中。
为了防止重复计数,依次计算满足以下条件的点集 \(S\) 数量,然后再求和。(不断移动正方体,一旦移动一定会有某些点不再位于正方体中,从而达到去重的目的)
- \(\min_{(x_i, y_i, z_i) \in S x_i} = x\)
- \(\min_{(x_i, y_i, z_i) \in S y_i} = y\)
- \(\min_{(x_i, y_i, z_i) \in S z_i} = z\)
将点集中的点 \((x_i, y_i, z_i)\) 分成 \(8\) 类:
- \(x_i\) 是否为 \(x\)
- \(y_i\) 是否为 \(y\)
- \(z_i\) 是否为 \(z\)
假设集合名中出现 \(X\) 就表示满足 \(x_i = x\) 的点, \(Y\) 和 \(Z\) 同理。出现多个大写字母就是多个点集的交。三个座标都不满足的点集为 \(O\)。
再手动去重算点集数量。
- 先将点集分成包含 \(XYZ\) 中点的和不包含的,包含了 \(XYZ\) 中点之后其他点都可以任意选,所以这一类数量为 \((2^{|XYZ|} - 1) 2^{|O| + |Z| + |Y| + |YZ| + |X| + |XZ| + |XY|}\)。
- 此后都不能包含 \(XYZ\) 了,将点分为包含 \(XY\) 中点的和不包含的,,包含了 \(XY\) 中的还要再 \(Z \cup YZ \cup XZ\) 中至少选一个点,然后 \(O \cup Y \cup X\) 中的点可以任选。
- 以此类推。
AC代码
// #define MULTIPLE_TASK
#include "hira/main.cpp"
#include "hira/math/modular/mod_int.h"
using mint = math::ModInt998;
#include "hira/std_extension/discretize.h"
void Initialize() {}
void SolveCase(int Case) {
int n;
i64 d;
std::cin >> n >> d;
std::vector<mint> p2(n + 1);
p2[0] = 1;
for (int i = 1; i <= n; ++i)
p2[i] = p2[i - 1] * 2;
std::vector<std::array<int, 3>> a(n);
std::vector<int> xx(n), yy(n), zz(n);
for (int i = 0; i < n; ++i) {
int x, y, z;
std::cin >> x >> y;
z = x - y;
a[i] = {x, y, z};
xx[i] = x;
yy[i] = y;
zz[i] = z;
}
std::sort(
a.begin(), a.end(),
[](const std::array<int, 3>& lhs, const std::array<int, 3>& rhs) -> bool {
return lhs[0] < rhs[0];
});
xx = stdext::discretize(xx);
yy = stdext::discretize(yy);
zz = stdext::discretize(zz);
auto calc = [&](const std::array<int, 8>& c) {
auto [none, z, y, yz, x, xz, xy, xyz] = c;
mint ret(0);
ret += (p2[xyz] - 1) * p2[none + z + y + yz + x + xz + xy];
ret += (p2[xy] - 1) * (p2[z + yz + xz] - 1) * p2[y + x + none];
ret += (p2[xz] - 1) * (p2[y + yz] - 1) * p2[z + x + none];
ret += (p2[x] - 1) * (p2[yz] - 1) * p2[none + z + y];
ret += (p2[x] - 1) * (p2[y] - 1) * (p2[z] - 1) * p2[none];
return ret;
};
std::vector<std::array<int, 3>> p(n);
auto solve = [&](int y, int z) {
mint ret(0);
int r = -1;
std::array<int, 8> c = {0};
int curr = 0;
p.clear();
for (int x : xx) {
// previous points lies on left bound of x coordinate now invalid.
// remove them.
for (int t = 0; t < 8; ++t) {
if (t & 0b100)
c[t] = 0;
}
// new points fit in the range.
while (r + 1 < n && a[r + 1][0] <= x + d) {
++r;
if (a[r][1] < y || a[r][1] > y + d || a[r][2] < z || a[r][2] > z + d)
continue;
++c[((a[r][1] == y) << 1) | (a[r][2] == z)];
p.push_back(a[r]);
}
// update points lies on left bound of x coordinate.
while (curr < p.size() && p[curr][0] == x) {
--c[((p[curr][1] == y) << 1) | (p[curr][2] == z)];
++c[0b100 | ((p[curr][1] == y) << 1) | (p[curr][2] == z)];
++curr;
}
ret += calc(c);
}
return ret;
};
mint ans(0);
for (int y : yy) {
for (int z : zz) {
ans += solve(y, z);
}
}
std::cout << ans.value() << "\n";
}
Ex - Substring Sort
To be solved.
标签:std,AtCoder,hira,Beginner,int,p2,280,include,mint From: https://www.cnblogs.com/zengzk/p/16950555.html