ABC340
E - Mancala 2
按照题意模拟区间加和即可,但不能直接差分,因为下一次发放球又要使用新的球数,所以需要用到数据结构。
-
线段树做法
- 比较无脑,就用一颗lazytagSegmentTree实现区间加,单点查。
template<class Info, class Tag> struct LazySegmentTree { int n; std::vector<Info> info; std::vector<Tag> tag; LazySegmentTree() : n(0) {} LazySegmentTree(int n_, Info v_ = Info()) { init(n_, v_); } template<class T> LazySegmentTree(std::vector<T> init_) { init(init_); } void init(int n_, Info v_ = Info()) { init(std::vector(n_, v_)); } template<class T> void init(std::vector<T> init_) { n = init_.size(); info.assign(4 << std::__lg(n), Info()); tag.assign(4 << std::__lg(n), Tag()); std::function<void(int, int, int)> build = [&](int p, int l, int r) { if (r - l == 1) { info[p] = init_[l]; return; } int m = (l + r) / 2; build(2 * p, l, m); build(2 * p + 1, m, r); pull(p); }; build(1, 0, n); } void pull(int p) { info[p] = info[2 * p] + info[2 * p + 1]; } void apply(int p, const Tag &v) { info[p].apply(v); tag[p].apply(v); } void push(int p) { apply(2 * p, tag[p]); apply(2 * p + 1, tag[p]); tag[p] = Tag(); } void modify(int p, int l, int r, int x, const Info &v) { if (r - l == 1) { info[p] = v; return; } int m = (l + r) / 2; push(p); if (x < m) { modify(2 * p, l, m, x, v); } else { modify(2 * p + 1, m, r, x, v); } pull(p); } void modify(int p, const Info &v) { modify(1, 0, n, p, v); } Info rangeQuery(int p, int l, int r, int x, int y) { if (l >= y || r <= x) { return Info(); } if (l >= x && r <= y) { return info[p]; } int m = (l + r) / 2; push(p); return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m, r, x, y); } Info rangeQuery(int l, int r) { return rangeQuery(1, 0, n, l, r); } void rangeApply(int p, int l, int r, int x, int y, const Tag &v) { if (l >= y || r <= x) { return; } if (l >= x && r <= y) { apply(p, v); return; } int m = (l + r) / 2; push(p); rangeApply(2 * p, l, m, x, y, v); rangeApply(2 * p + 1, m, r, x, y, v); pull(p); } void rangeApply(int l, int r, const Tag &v) { return rangeApply(1, 0, n, l, r, v); } }; struct Tag { i64 add = 0; void apply(Tag t) { add += t.add; } }; struct Info { i64 x = 0; int r, l; Info(){} Info(i64 _x, int l, int r):x(_x), l(l), r(r){} void apply(Tag t) { x += t.add * (r - l); } }; Info operator+(Info a, Info b) { return {a.x + b.x, a.l, b.r}; } signed main() { std::cin.tie(nullptr)->sync_with_stdio(false); int n, m; std::cin >> n >> m; std::vector<Info> a(n); for (int i = 0; i < n; i++) { std::cin >> a[i].x; a[i].l = i; a[i].r = i + 1; } LazySegmentTree<Info, Tag> T(a); for (int i = 0, x; i < m; i++) { std::cin >> x; i64 now = T.rangeQuery(x, x + 1).x; T.modify(x, {0, x, x + 1});//先把原来的位置清空 T.rangeApply(0, n, {now / n});//平均发放n轮 int y = x + (now % n); if (y < n) { T.rangeApply(x + 1, y + 1, {1}); } else { T.rangeApply(x + 1, n, {1}); T.rangeApply(0, y - n + 1, {1}); } } for (int i = 0; i < n; i++) {std::cout << T.rangeQuery(i, i + 1).x << ' ';} std::cout << '\n'; return 0; }
-
树状数组维护差分
- 用BIT维护一个差分数组
- 区间加直接用差分来解决
- 单点查再利用树状数组快速查询区间和的性质,对差分数组快速求和。
- 用BIT维护一个差分数组
template<class T> struct BIT {
int n;
std::vector<T> w;
BIT(int n) : n(n), w(n + 1) {}
void add(int x, T v) {for (; x <= n; x += x & -x) {w[x] += v;}}
T ask(int x) {T ans = 0; for (; x; x -= x & -x) {ans += w[x];} return ans;}
T ask(int l, int r) {return ask(r) - ask(l - 1);}
};
signed main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
int n, m;
std::cin >> n >> m;
std::vector<int> a(n);
for (auto& x : a) {std::cin >> x;}
BIT<i64> bitT(n + 1);
for (int i = 0; i < n; i++) {//BIT维护差分数组,BITfrom1
bitT.add(i + 1, a[i]); bitT.add(i + 1 + 1, -a[i]);
}
for (int i = 0, x; i < m; i++) {
std::cin >> x; ++x;
i64 now = bitT.ask(x);//差分和出当前位置的球数量
bitT.add(x, -now); bitT.add(x + 1, now);//把当前的球移除
bitT.add(1, now / n);//先平均分配n圈
int y = x + now % n;//剩下最后一批分配完后的下标
if (y <= n) {
bitT.add(x + 1, 1);
bitT.add(y + 1, -1);
} else {
bitT.add(x + 1, 1);
bitT.add(1, 1);
bitT.add(y - n + 1, -1);
}
}
for (int i = 1; i <= n; i++) {std::cout << bitT.ask(i) << ' ';}
std::cout << '\n';
return 0;
}
F - S = 1
顶点为 \((0, 0), (X, Y)\) 和 \((A, B)\) 的三角形的面积是 \(\frac{\vert AY - BX \vert}{2}\) 。因此我们可以认为,在给定整数 \(X\) 和 \(Y\) 的情况下,这个问题要求找到一对整数 \((A, B)\) ,使得
我们将解释如何解决这个问题。在这里,我们设 \(g = \gcd(X, Y)\) .(注: \(\gcd(a, b)\) 被定义为 \(\vert a \vert\) 和 \(\vert b \vert\) 的最大公约数)。
如果 \(g\) 是 \(3\) 或更大,那么答案就不存在,因为 \(AY - BX\) 总是 \(g\) 的倍数。
如果 \(g = 1, 2\) ,那么我们总是可以使用一种叫做扩展欧氏算法的算法来求解。扩展欧氏算法是这样一种算法:输入整数对 \((a, b)\) ,在 \(\mathrm{O}(\log \min(|a|, |b|))\) 时间内找到一个整数对 \((x, y)\) ,使得 \(ax + by = \pm \mathrm{gcd}(a, b)\) 。(这里,保证整数对 \(x, y\) 是满足 \(\max(|x|, |y|) \leq \max(|a|, |b|)\) 的整数)。
将 \((Y, -X)\) 作为扩展欧几里得算法的输入,可以得到一对 \((c, d)\) ,即
和 \(\vert c \vert, \vert d \vert \leq 10^{17}\) 作为返回值。我们最初想要的是一对 \((A, B)\) ,即 \(AY - BX =\pm 2\) ,可以通过将 \(c\) 和 \(d\) 乘以 \(\frac{2}{g}\) 得到。这个 \((A, B)\) 安全地满足 \(\vert A \vert, \vert B \vert \leq 10^{18}\) 。
因此,问题已经解决。计算复杂度约为 \(\mathrm{O}(\log \min (X, Y))\) ,足够快了。
i64 exgcd(i64 a, i64 b, i64 &x, i64 &y) {
if (!b) {
x = 1, y = 0;
return a;
}
i64 g = exgcd(b, a % b, y, x);
y -= a / b * x;
return g;
}
signed main() {
std::cin.tie(nullptr)->sync_with_stdio(false);
i64 x, y;//给定整数x, y
std::cin >> x >> y;
i64 c{}, d{};//即exgcd的解
i64 g = exgcd(y, x, c, d);
if (2 % g != 0) {std::cout << -1 << '\n';}
else {
c *= 2 / g; d *= -2 / g;
std::cout << c << ' ' << d << '\n';
}
return 0;
}