A就是模拟一下。
B就是曼哈顿距离。
C就是std::map
搞一搞。
D就是容斥一下。
E - Distance Sequence
题意
问满足下列条件的数组\(a\)有多少种:
- 长度为\(n\)
- 值域为\([1, m]\)
- 相邻元素差值的绝对值大于\(k\)
其中\(n \le 1000, m \le 5000, k \le m - 1\)。
思路
\(O(nm)\)的DP就能搞定。
令\(dp_{i, j}\)表示长度为\(i\),满足题目给定条件,且\(a_i = j\)的方案数。
那么有\(dp_{i, j} = \sum_{|x - j| \ge k} dp_{i - 1, x}\),朴素的做法是\(O(nm^2)\)的。
注意到其实满足条件的\(x\)其实是\([1, i - k] \cup [i + k, m]\),加个前缀和优化就能做到\(O(nm)\)了。
AC代码
// Problem: E - distance Sequence
// Contest: AtCoder - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
// URL: https://atcoder.jp/contests/abc253/tasks/abc253_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif
using i64 = int64_t;
using u64 = uint64_t;
void solve_case(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
solve_case(t);
}
return 0;
}
void solve_case(int Case) {
int mod = 998244353;
int n, m, k;
std::cin >> n >> m >> k;
if (k == 0) {
int ans = 1;
for (int i = 1; i <= n; ++i)
ans = i64(1) * ans * m % mod;
std::cout << ans << "\n";
return;
}
std::vector<int> dp(m + 1);
for (int i = 1; i <= m; ++i)
dp[i] = 1;
for (int _ = 2; _ <= n; ++_) {
auto temp = dp;
for (int j = 1; j <= m; ++j)
temp[j] = (temp[j] + temp[j - 1]) % mod;
for (int j = 1; j <= m; ++j) {
dp[j] = 0;
if (j - k >= 1) {
dp[j] = (dp[j] + ((temp[j - k] - temp[0]) % mod + mod) % mod) % mod;
}
if (j + k <= m) {
dp[j] = (dp[j] + ((temp[m] - temp[j + k - 1]) % mod + mod) % mod) % mod;
}
}
}
int ans = 0;
for (int i = 1; i <= m; ++i)
ans = (ans + dp[i]) % mod;
std::cout << ans << "\n";
}
F - Operations on a Matrix
思路1
对于第1个操作,用可持久化线段树维护。
对于第2个操作,操作时记录下当时可持久化线段树的版本号以及\(x\)。
对于第3个操作,假设第\(i\)行维护的版本号为\(old\),操作数为\(x\)。当前的版本号为\(Now\),则答案为\(x + sum(Now, j, j) - sum(old, j, j)\)。其中\(sum(v, l, r)\)表示可持久化线段树第\(i\)个版本中区间\([l, r]\)元素之和。
然后就是可持久化线段树区间加操作的维护了,指路HDU 4348。
思路2
可以借助离散化进一步简化问题。
对于每个操作3,它其实只和上一次修改第\(i\)行的操作2有关,可以把操作3挂到操作2上。
假设某个操作2\((i, x)\)之前,操作1在第\(j\)行上共加了\(A\)。对于某个操作3\((i, j)\),假设上一句提到的\((i, x)\)为该操作3前最后一个修改第\(i\)行,且此时操作1在第\(j\)行上共加了\(B\),那么该操作3的答案为\(x + B - A\)。
\(B\)的话临时查寻即可,\(x\)和\(B\)的话就是把操作3挂到操作2上,然后在操作2操作的时候就加上。
现在就不需要可持久化了,普通线段树或者树状数组都可以搞。
思路1 AC代码
// Problem: F - Operations on a Matrix
// Contest: AtCoder - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
// URL: https://atcoder.jp/contests/abc253/tasks/abc253_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif
using i64 = int64_t;
using u64 = uint64_t;
void solve_case(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
solve_case(t);
}
return 0;
}
// persistant segment tree with lazy tag.
const int maxn = 2e5 + 5;
const int SIZE = maxn << 5;
#define ll long long
#define lson l, m
#define rson m + 1, r
int rt[maxn], ls[SIZE], rs[SIZE], sz;
ll sum[SIZE], lazy[SIZE];
void pushup(int rt, int m) {
sum[rt] = sum[ls[rt]] + sum[rs[rt]] + (ll)lazy[rt] * m;
}
void build(int& rt, int l, int r) {
rt = ++sz;
lazy[rt] = 0;
if (l == r) {
sum[rt] = 0;
return;
}
int m = (l + r) >> 1;
build(ls[rt], lson);
build(rs[rt], rson);
pushup(rt, r - l + 1);
}
void update(int pre, int& rt, int L, int R, int l, int r, ll c) {
rt = ++sz;
lazy[rt] = lazy[pre];
sum[rt] = sum[pre];
ls[rt] = ls[pre];
rs[rt] = rs[pre];
if (L <= l && r <= R) {
lazy[rt] += c;
sum[rt] += (ll)c * (r - l + 1);
return;
}
int m = (l + r) >> 1;
if (L <= m)
update(ls[pre], ls[rt], L, R, lson, c);
if (R > m)
update(rs[pre], rs[rt], L, R, rson, c);
pushup(rt, r - l + 1);
}
ll query(int rt, int L, int R, int l, int r) {
if (L <= l && r <= R) {
return sum[rt];
}
ll ret = (ll)lazy[rt] * (R - L + 1);
int m = (l + r) >> 1;
if (R <= m)
ret += query(ls[rt], L, R, lson);
else if (L > m)
ret += query(rs[rt], L, R, rson);
else
ret += query(ls[rt], L, m, lson) + query(rs[rt], m + 1, R, rson);
return ret;
}
void solve_case(int Case) {
int n, m, q;
std::cin >> n >> m >> q;
int version = 0;
sz = 0;
build(rt[version], 1, m);
std::vector<std::pair<int, int>> row(n + 1, std::pair(0, 0));
for (int i = 1; i <= q; ++i) {
int op;
std::cin >> op;
if (op == 1) {
int l, r, x;
std::cin >> l >> r >> x;
++version;
update(rt[version - 1], rt[version], l, r, 1, m, x);
} else if (op == 2) {
int p, x;
std::cin >> p >> x;
row[p] = std::pair<int, int>(x, version);
} else if (op == 3) {
int x, y;
std::cin >> x >> y;
int old_version = row[x].second;
ll ans = row[x].first + query(rt[version], y, y, 1, m) - query(rt[old_version], y, y, 1, m);
std::cout << ans << "\n";
}
}
}
思路2 AC代码
// Problem: F - Operations on a Matrix
// Contest: AtCoder - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
// URL: https://atcoder.jp/contests/abc253/tasks/abc253_f
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif
using i64 = int64_t;
using u64 = uint64_t;
void solve_case(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
solve_case(t);
}
return 0;
}
template <typename T>
struct FenwickTree {
int n;
std::vector<T> c;
FenwickTree(int _n) : n(_n), c(n + 1) {}
inline int lb(int x) { return x & -x; }
void add(int x, T d) {
for (; x <= n; x += lb(x))
c[x] += d;
}
T getsum(int x) {
T r = 0;
for (; x; x -= lb(x))
r += c[x];
return r;
}
T getsum(int l, int r) { return getsum(r) - getsum(l - 1); }
};
void solve_case(int Case) {
int n, m, q;
std::cin >> n >> m >> q;
FenwickTree<i64> t(m);
std::vector<std::array<int, 4>> a(q + 1);
std::vector<std::vector<int>> QueryAt(q + 1);
std::vector<int> last(n + 1);
std::vector<i64> ans;
for (int i = 1; i <= q; ++i) {
int op;
std::cin >> op;
if (op == 1) {
int l, r, d;
std::cin >> l >> r >> d;
a[i] = {op, l, r, d};
} else if (op == 2) {
int x, d;
std::cin >> x >> d;
a[i] = {op, x, d, i};
last[x] = i;
} else if (op == 3) {
int x, y;
std::cin >> x >> y;
int ans_id = ans.size();
ans.push_back(0);
a[i] = {op, x, y, ans_id};
int last_x = last[x];
QueryAt[last_x].push_back(i);
}
}
for (int i = 1; i <= q; ++i) {
int op = a[i][0];
if (op == 1) {
auto [_, l, r, d] = a[i];
t.add(l, d);
t.add(r + 1, -d);
} else if (op == 2) {
auto [_1, _2, d, _3] = a[i];
for (int query_id : QueryAt[i]) {
auto [_1, _2, y, ans_id] = a[query_id];
ans[ans_id] += d;
ans[ans_id] -= t.getsum(y);
}
} else if (op == 3) {
auto [_1, _2, y, ans_id] = a[i];
ans[ans_id] += t.getsum(y);
}
}
for (int i = 0; i < ans.size(); ++i)
std::cout << ans[i] << "\n";
}
G - Swap Many Times
题意
将所有满足\(1 \le x < y \le n\)的二元组按字典序排序,其中第\(i\)个记为\((x_i, y_i)\)。
现在给定一个长度为\(n\)的数组\(a_i = i\),给定\(L, R\),对于\(i = L \dots R\),依次对换\(a_{x_i}和a_{y_i}\)的值。
输出操作完之后的\(a\)。
思路1
对于二元组\((x_i, y_i)\),将其放到第\(x_i\)行。
特判需要操作的区间占某一行中间的情况。
对于剩余情况,可以发现需要操作的二元组可以分成3个部分:第\(x\)行末尾,第\(x+1\)行到\(y-1\)行全部,和第\(y\)行的开头。
观察1:如果第\(i\)行的二元组都操作,相当于将\(a_i,\dots a_n\)循环右移。有\(O(n)\)行的二元组满足行中二元组都要操作。
观察2:剩余操作至多占两行,也就是\(O(n)\)个操作。
借助平衡树维护区间信息,两个操作都可以单次\(O(\log n)\)搞,总的时间复杂度为\(O(n \log n)\)。
思路2
在思路1的基础上进行优化。
观察3:第\(x+1\)行到\(y-1\)行全都操作,相当于翻转\(a_{x+1} \dots a_n\),然后再翻转\(a_{y} \dots a_n\)。
根据这个就可以\(O(n)\)搞了,第\(x\)行末尾暴力搞,第\(x+1\)行到\(y-1\)行的操作拆成了两次翻转,第\(y\)行开头暴力搞,总的时间复杂度为\(O(n)\)。
思路1 AC代码
// Problem: G - Swap Many Times
// Contest: AtCoder - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
// URL: https://atcoder.jp/contests/abc253/tasks/abc253_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif
using i64 = int64_t;
using u64 = uint64_t;
void solve_case(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
solve_case(t);
}
return 0;
}
std::mt19937 rng(std::chrono::steady_clock::Now().time_since_epoch().count());
template <typename T>
struct Treap {
struct node {
node *l, *r;
unsigned rnd;
T v;
int sz;
node(T _v) : l(NULL), r(NULL), rnd(rng()), sz(1), v(_v) {}
};
inline int get_size(node*& p) { return p ? p->sz : 0; }
inline void push_up(node*& p) {
if (!p)
return;
p->sz = get_size(p->l) + get_size(p->r) + 1;
}
node* root = NULL;
node* merge(node* a, node* b) {
if (!a)
return b;
if (!b)
return a;
if (a->rnd < b->rnd) {
a->r = merge(a->r, b);
push_up(a);
return a;
} else {
b->l = merge(a, b->l);
push_up(b);
return b;
}
}
void split_val(node* p, const T& k, node*& a, node*& b) {
if (!p)
a = b = NULL;
else {
if (p->v <= k) {
a = p;
split_val(p->r, k, a->r, b);
push_up(a);
} else {
b = p;
split_val(p->l, k, a, b->l);
push_up(b);
}
}
}
void split_size(node* p, int k, node*& a, node*& b) {
if (!p)
a = b = NULL;
else {
if (get_size(p->l) < k) {
a = p;
split_size(p->r, k - get_size(p->l) - 1, a->r, b);
push_up(a);
} else {
b = p;
split_size(p->l, k, a, b->l);
push_up(b);
}
}
}
void ins(int p, T val) {
node *a, *b;
split_size(root, p - 1, a, b);
a = merge(a, new node(val));
root = merge(a, b);
}
void del(T val) {
node *a, *b, *c, *d;
split_val(root, val, a, b);
split_val(a, val - 1, c, d);
node* e = d;
d = merge(d->l, d->r);
delete e;
a = merge(c, d);
root = merge(a, b);
}
T qry(int p) {
node *a, *b, *c, *d;
split_size(root, p - 1, a, b);
split_size(b, 1, c, d);
T result = c->v;
b = merge(c, d);
root = merge(a, b);
return result;
}
void right_rotate(int l, int r) {
node *a, *b, *c, *d;
split_size(root, l - 1, a, b);
split_size(b, r - l + 1, c, d);
{
node *e, *f;
split_size(c, r - l, e, f);
c = merge(f, e);
}
b = merge(c, d);
root = merge(a, b);
}
void swap(int l, int r) {
node *a, *b, *c, *d;
split_size(root, l - 1, a, b);
split_size(b, r - l + 1, c, d);
{
node *e, *f;
split_size(c, r - l, e, f);
node *g, *h;
split_size(e, 1, g, h);
e = merge(f, h);
c = merge(e, g);
}
b = merge(c, d);
root = merge(a, b);
}
void debug() {
#ifdef BACKLIGHT
std::function<void(node*)> dfs = [&](node* p) {
if (!p)
return;
dfs(p->l);
std::cerr << to_string(p->v) << " ";
dfs(p->r);
};
dfs(root);
std::cerr << std::endl;
#endif
}
};
void solve_case(int Case) {
int n;
std::cin >> n;
i64 l, r;
std::cin >> l >> r;
Treap<int> t;
for (int i = 1; i <= n; ++i)
t.ins(i, i);
t.debug();
i64 L = 1, R = 0;
int len = n - 1;
for (int i = 1; i <= n - 1; ++i) {
L = R + 1;
R = L + len - 1;
--len;
if (R < l)
continue;
if (L > r)
continue;
if (L >= l && R <= r) {
t.right_rotate(i, n);
logd("S", i);
t.debug();
continue;
}
int j = i + 1;
i64 y = L;
while (y < l) {
++j;
++y;
}
while (y <= r && y <= R) {
t.swap(i, j);
++j;
++y;
}
}
for (int i = 1; i <= n; ++i)
std::cout << t.qry(i) << " \n"[i == n];
}
思路2 AC代码
// Problem: G - Swap Many Times
// Contest: AtCoder - NOMURA Programming Contest 2022(AtCoder Beginner Contest 253)
// URL: https://atcoder.jp/contests/abc253/tasks/abc253_g
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define CPPIO std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0);
#define freep(p) p ? delete p, p = nullptr, void(1) : void(0)
#ifdef BACKLIGHT
#include "debug.h"
#else
#define logd(...) ;
#endif
using i64 = int64_t;
using u64 = uint64_t;
void solve_case(int Case);
int main(int argc, char* argv[]) {
CPPIO;
int T = 1;
// std::cin >> T;
for (int t = 1; t <= T; ++t) {
solve_case(t);
}
return 0;
}
void solve_case(int Case) {
int n;
std::cin >> n;
i64 l, r;
std::cin >> l >> r;
std::vector<int> a(n + 1);
for (int i = 1; i <= n; ++i)
a[i] = i;
i64 L = 1, R = 0;
int len = n - 1;
int st = -1, ed = -1, flag = 0;
for (int i = 1; i <= n - 1; ++i) {
L = R + 1;
R = L + len - 1;
--len;
if (R < l)
continue;
if (L > r)
continue;
if (L >= l && R <= r) {
if (st == -1)
st = i;
ed = i;
logd(st, ed);
continue;
}
if (flag == 0 && st != -1) {
std::reverse(a.begin() + st, a.end());
std::reverse(a.begin() + ed + 1, a.end());
flag = 1;
}
int j = i + 1;
i64 y = L;
while (y < l) {
++j;
++y;
}
while (y <= r && y <= R) {
std::swap(a[i], a[j]);
++j;
++y;
}
}
if (flag == 0 && st != -1) {
std::reverse(a.begin() + st, a.end());
std::reverse(a.begin() + ed + 1, a.end());
flag = 1;
}
for (int i = 1; i <= n; ++i)
std::cout << a[i] << " \n"[i == n];
}
Ex - We Love Forest
TBA。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。