T1
前缀和 + 二分即可,再考虑一下左右端点还在睡觉的情况。
// Problem: D - Sleep Log
// Contest: AtCoder - KYOCERA Programming Contest 2023(AtCoder Beginner Contest 305)
// URL: https://atcoder.jp/contests/abc305/tasks/abc305_d?lang=en
// Memory Limit: 1024 MB
// Time Limit: 3000 ms
// Good luck to the code >w<
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define open(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define abs(x) (((x) > (0)) ? (x) : (-(x)))
#define print(x) printf("%d %d\n", x.first, x.second)
const int maxn = 2e5 + 5;
int a[maxn], n, sum[maxn];
int main() {scanf("%d", &n);for (int i = 1; i <= n; i ++) scanf("%d", &a[i]), (i % 2 == 0) ? sum[i] = sum[i - 1] : sum[i] = sum[i - 1] + a[i] - a[i - 1];int Q; scanf("%d", &Q);for (int i = 1, l, r; i <= Q; i ++) {scanf("%d %d", &l, &r);int L = lower_bound(a + 1, a + n + 1, l) - a, R = upper_bound(a + 1, a + n + 1, r) - a - 1;int ans = 0;if (L & 1) ans += a[L] - l;if (R % 2 == 0) ans += r - a[R];printf("%d\n", ans + sum[R] - sum[L]);}return 0;
}
T2
观察到我们并不关心 i i i 点被哪个特殊点覆盖,而关心走到 i i i 时最多还剩下多少步用于继续扩展。我们以每个特殊点为起点、开始的权值为 h i h_i hi;将边权设为 − 1 -1 −1,然后跑最长路(此时可以用 Dijkstra 求解,负权最长路就是正权最短路)即可。
// Problem: E - Art Gallery on Graph
// Contest: AtCoder - KYOCERA Programming Contest 2023(AtCoder Beginner Contest 305)
// URL: https://atcoder.jp/contests/abc305/tasks/abc305_e?lang=en
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// Good luck to the code >w<
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define open(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define abs(x) (((x) > (0)) ? (x) : (-(x)))
#define print(x) printf("%d %d\n", x.first, x.second)
const int maxn = 2e5 + 5;
int n, m, k;
namespace Graph {struct Edge { int to, nxt; } e[maxn << 1];int head[maxn], ecnt;void addEdge(int u, int v) {e[++ ecnt] = Edge { v, head[u] };head[u] = ecnt;}
} using namespace Graph;
priority_queue<pair<int, int> >q;
#define mk make_pair
bool vis[maxn]; int dis[maxn];
int main() {scanf("%d %d %d", &n, &m, &k);for (int i = 1; i <= n; i ++)dis[i] = -1;for (int i = 1, u, v; i <= m; i ++) {scanf("%d %d", &u, &v);addEdge(u, v), addEdge(v, u);} for (int i = 1, u, w; i <= k; i ++)scanf("%d %d", &u, &w), dis[u] = w, q.push(mk(w, u));int ans = 0;while (!q.empty()) {auto [w, u] = q.top(); q.pop();if (vis[u]) continue; vis[u] = 1, ans ++;if (w == 0) continue;for (int i = head[u]; i; i = e[i].nxt) {int v = e[i].to;if (dis[v] < dis[u] - 1) {dis[v] = dis[u] - 1;if (!vis[v]) q.push(mk(dis[v], v));}}} printf("%d\n", ans);for (int i = 1; i <= n; i ++)if (vis[i]) printf("%d ", i);return 0;
}
T3
解法1 - 逆序对
首先若存在一个 v v v 使得 a a a 中 v v v 的出现次数不等于 v v v 在 b b b 中的出现次数,那么直接无解。我们考虑一次操作的影响,若元素没有重复出现,那么右移一次会增加 / 减少 2 2 2 对逆序对或者不变,即逆序对的变化量总是偶数。那么若 a a a 中逆序对数量与 b b b 中逆序对数量奇偶性相同,那么就可以使得 a a a 变成 b b b。再考虑 a a a 或 b b b 中出现相同元素的影响,对于一对相同的元素 ( a i , a j ) (a_i,a_j) (ai,aj),其中 i < j i<j i<j;我们不妨给它们加上一个优先级,值相同时优先级更大的元素更大;那么若优先级给 a i a_i ai 大一些,那么 ( a i , a j ) (a_i,a_j) (ai,aj) 就是一对逆序对,反之则不是。这么操作可以自由决定最后 a a a 的逆序对数量的奇偶性,即最终一定有解。貌似在有交换操作的题目中逆序对总是一个切入口。
解法2 - 随机化
我做的时候不知道用逆序对和是否有元素相同判断,那怎么做呢?我们直接模拟将 a a a 变成 b b b 的过程,从大到小枚举 i i i 使得 a i = b i a_i=b_i ai=bi,每一轮我们在 [ 1 , i ] [1,i] [1,i] 中选出一个 j j j 使得 a j = b i a_j=b_i aj=bi,然后用若干次右移操作把 a j a_j aj 提到 a i a_i ai 的位置;等到剩下 3 3 3 个元素时,枚举可能的三种排列情况,判断是否与 b b b 的前三个剩的元素相等即可。这个做法的问题在于在出现相同元素时,到底把谁拉上来。我们不妨随机一个元素拉上来,我的做法中优先选择了离 i i i 近一些的元素。这么做正确率可能低,那么我们多做几轮,我做了 15 15 15 轮就过了。正确率未知。
// Problem: B - Triple Shift
// Contest: AtCoder - AtCoder Regular Contest 136
// URL: https://atcoder.jp/contests/arc136/tasks/arc136_b?lang=en
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// Good luck to the code >w<
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define open(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define abs(x) (((x) > (0)) ? (x) : (-(x)))
#define print(x) printf("%d %d\n", x.first, x.second)
const int maxn = 5005;
int a[maxn], b[maxn], n;
int t1[maxn], t2[maxn];
int p[maxn];
int main() {scanf("%d", &n), srand(time(0));for (int i = 1; i <= n; i ++)scanf("%d", &a[i]), t1[a[i]] ++;for (int i = 1; i <= n; i ++)scanf("%d", &b[i]), t2[b[i]] ++;for (int i = 1; i <= 5000; i ++)if (t1[i] != t2[i]) return puts("No"), 0;for (int tt = 0; tt <= 15; tt ++) {for (int i = n, pos; i >= 3; i --) {pos = -1;for (int j = i; j; j --)if (a[j] == b[i]) {if (pos == -1) pos = j;else if (rand() % 3 == 2) pos = j;}for (; pos + 2 <= i; pos += 2) {int a0 = a[pos], a1 = a[pos + 1], a2 = a[pos + 2];a[pos] = a1, a[pos + 1] = a2, a[pos + 2] = a0;} if (pos != i) {int a0 = a[pos - 1], a1 = a[pos], a2 = a[pos + 1];a[pos - 1] = a2, a[pos] = a0, a[pos + 1] = a1;}}int a0 = a[1], a1 = a[2], a2 = a[3];if (a0 == b[1] && a1 == b[2] && a2 == b[3])return puts("Yes"), 0;else if (a2 == b[1] && a0 == b[2] && a1 == b[3])return puts("Yes"), 0;else if (a1 == b[1] && a2 == b[2] && a0 == b[3])return puts("Yes"), 0;} puts("No");return 0;
}
T4
观察一次操作带来的影响。显然答案至少是 max { a i } \max\{a_i\} max{ai},我们记这个值为 m m m,但是在操作中点权被减到 0 0 0 的元素会将环断成若干段,答案不一定能取到 m m m。区间减法启发我们考虑差分,令 d i = ∣ a i − a i + 1 ∣ d_i=|a_i-a_{i+1}| di=∣ai−ai+1∣( d n = ∣ a n − a 1 ∣ d_n=|a_n-a_1| dn=∣an−a1∣),发现一次操作最多使 ∑ d i \sum d_i ∑di 减二。于是答案至少是 ∑ d i 2 \cfrac{\sum d_i}{2} 2∑di,我们记这个值为 k k k,但是若选择整个环进行操作,那么答案不一定能取到 k k k。结论就是,答案等于 max ( m , k ) \max(m,k) max(m,k)。
- 首先,若 k = 0 k=0 k=0,那么 a a a 一定全部相等,做若干次全局减一即可,答案为 m m m。
- 否则,若 k < m k<m k<m,那么 m > 0 m>0 m>0,不断做全局减一使得 m ← m − 1 m\gets m-1 m←m−1 而 k k k 显然不变,直到 m = k m=k m=k,该情况留到后面讨论。
- 若 k > m k>m k>m,观察到 a a a 的初始值 > 0 >0 >0,我们考虑不断选择仅包含 m m m 的区间 − 1 -1 −1,这样 m m m 的变化量不大于 k k k 的变化量且可以保证全 0 0 0 段的数量 ≤ 1 \le 1 ≤1,也是一直做直到 m = k m=k m=k。
- 若 k = m k=m k=m,进行分类讨论:
- 若当前只存在一个极长的仅包含 m m m 的连续段,那么我们将这个连续段直接 − 1 -1 −1 使 m , k m,k m,k 均减一。
- 否则 a a a 中一定没有 0 0 0,我们找到最短的包含了所有值为 m m m 的元素的区间 − 1 -1 −1,效果同上。
// Problem: [ARC136C] Circular Addition
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/AT_arc136_c
// Memory Limit: 1 MB
// Time Limit: 2000 ms
// Good luck to the code >w<
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define open(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define abs(x) (((x) > (0)) ? (x) : (-(x)))
#define print(x) printf("%d %d\n", x.first, x.second)
const int maxn = 2e5 + 5;
int a[maxn], mx; ll d;
int main() {int n; scanf("%d", &n);for (int i = 1; i <= n; i ++)scanf("%d", &a[i]), mx = max(mx, a[i]), d += abs(a[i] - a[i - 1]);d = (d + abs(a[n] - a[1]) - a[1]) >> 1, printf("%lld\n", max(1ll * mx, d));return 0;
}
T5
把每个 a i a_i ai 拆成 6 6 6 元组,那么实际上就是统计每一项都比 9 9 9 减 a i a_i ai 对应项小的六元组数量。直接高维前缀和即可,因为每一维只有 10 10 10 直接暴力做 6 6 6 遍即可。正确性显然。
// Problem: [ARC136D] Without Carry
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/AT_arc136_d
// Memory Limit: 1 MB
// Time Limit: 4000 ms
// Good luck to the code >w<
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define open(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define abs(x) (((x) > (0)) ? (x) : (-(x)))
#define print(x) printf("%d %d\n", x.first, x.second)
const int N = 10, maxn = 1e6 + 5;
ll f[N][N][N][N][N][N];
int tmp[N], a[maxn];
int main() {int n; scanf("%d", &n);for (int i = 1, x; i <= n; i ++) {scanf("%d", &x), a[i] = x;for (int j = 0; j < 6; j ++)tmp[j] = x % 10, x /= 10;f[tmp[0]][tmp[1]][tmp[2]][tmp[3]][tmp[4]][tmp[5]] ++;}for (int i0 = 0; i0 <= 9; i0 ++)for (int i1 = 0; i1 <= 9; i1 ++)for (int i2 = 0; i2 <= 9; i2 ++)for (int i3 = 0; i3 <= 9; i3 ++)for (int i4 = 0; i4 <= 9; i4 ++)for (int i5 = 1; i5 <= 9; i5 ++)f[i0][i1][i2][i3][i4][i5] += f[i0][i1][i2][i3][i4][i5 - 1];for (int i0 = 0; i0 <= 9; i0 ++)for (int i1 = 0; i1 <= 9; i1 ++)for (int i2 = 0; i2 <= 9; i2 ++)for (int i3 = 0; i3 <= 9; i3 ++)for (int i4 = 1; i4 <= 9; i4 ++)for (int i5 = 0; i5 <= 9; i5 ++)f[i0][i1][i2][i3][i4][i5] += f[i0][i1][i2][i3][i4 - 1][i5];for (int i0 = 0; i0 <= 9; i0 ++)for (int i1 = 0; i1 <= 9; i1 ++)for (int i2 = 0; i2 <= 9; i2 ++)for (int i3 = 1; i3 <= 9; i3 ++)for (int i4 = 0; i4 <= 9; i4 ++)for (int i5 = 0; i5 <= 9; i5 ++)f[i0][i1][i2][i3][i4][i5] += f[i0][i1][i2][i3 - 1][i4][i5];for (int i0 = 0; i0 <= 9; i0 ++)for (int i1 = 0; i1 <= 9; i1 ++)for (int i2 = 1; i2 <= 9; i2 ++)for (int i3 = 0; i3 <= 9; i3 ++)for (int i4 = 0; i4 <= 9; i4 ++)for (int i5 = 0; i5 <= 9; i5 ++)f[i0][i1][i2][i3][i4][i5] += f[i0][i1][i2 - 1][i3][i4][i5];for (int i0 = 0; i0 <= 9; i0 ++)for (int i1 = 1; i1 <= 9; i1 ++)for (int i2 = 0; i2 <= 9; i2 ++)for (int i3 = 0; i3 <= 9; i3 ++)for (int i4 = 0; i4 <= 9; i4 ++)for (int i5 = 0; i5 <= 9; i5 ++)f[i0][i1][i2][i3][i4][i5] += f[i0][i1 - 1][i2][i3][i4][i5];for (int i0 = 1; i0 <= 9; i0 ++)for (int i1 = 0; i1 <= 9; i1 ++)for (int i2 = 0; i2 <= 9; i2 ++)for (int i3 = 0; i3 <= 9; i3 ++)for (int i4 = 0; i4 <= 9; i4 ++)for (int i5 = 0; i5 <= 9; i5 ++)f[i0][i1][i2][i3][i4][i5] += f[i0 - 1][i1][i2][i3][i4][i5];ll ans = 0, tot = 0;for (int i = 1; i <= n; i ++) {int x = a[i];for (int j = 0; j < 6; j ++)tmp[j] = x % 10, x /= 10;ans += f[9 - tmp[0]][9 - tmp[1]][9 - tmp[2]][9 - tmp[3]][9 - tmp[4]][9 - tmp[5]];if (tmp[0] <= 4 && tmp[1] <= 4 && tmp[2] <= 4 && tmp[3] <= 4 && tmp[4] <= 4 && tmp[5] <= 4)tot ++;}printf("%lld\n", (ans - tot) >> 1);return 0;
}
T6
场上不会。显然 1 1 1 能直接算进答案中。对于点 i i i,记 p ( i ) p(i) p(i) 表示 i i i 的最小质因子;它能走到的最小点和能被走到的最大点为 i ± p ( i ) i\pm p(i) i±p(i);这两个点显然一定是偶数,而偶数间两两可达,于是对于 ( i , j ) (i,j) (i,j)( i < j i<j i<j):
- 若 i , j i,j i,j 为偶数,那么 i i i 可以走到 j j j;
- 若 i i i 为偶数 j j j 为奇数,那么当 i ≤ j − p ( j ) i\le j-p(j) i≤j−p(j) 时 i i i 能走到 j j j;
- 若 i i i 为奇数 j j j 为偶数,那么当 i + p ( i ) ≤ j i+p(i)\le j i+p(i)≤j 时 i i i 能走到 j j j;
- 若 i , j i,j i,j 为奇数,那么当 i + p ( i ) ≤ j − p ( j ) i+p(i)\le j-p(j) i+p(i)≤j−p(j) 时 i i i 能走到 j j j;
- 其余情况 i i i 均不能到达 j j j。
这里我们都以偶数可达的性质做了判断。于是我们将每个点视为一个区间,对于点 i i i,若 i i i 为偶数那么它对应的区间为 [ i , i ] [i,i] [i,i],否则为 [ i − p ( i ) + 1 , i + p ( i ) − 1 ] [i-p(i)+1,i+p(i)-1] [i−p(i)+1,i+p(i)−1];最终要求选出若干区间,使得它们的交集不是空集;若是空集则说明存在一对可达的点。我们将每个区间做一次区间 + A i +A_i +Ai,最后求一次全局 max \max max 即可。
// Problem: E - Non-coprime DAG
// Contest: AtCoder - AtCoder Regular Contest 136
// URL: https://atcoder.jp/contests/arc136/tasks/arc136_e
// Memory Limit: 1024 MB
// Time Limit: 2000 ms
// Good luck to the code >w<
//
// Powered by CP Editor (https://cpeditor.org)#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define open(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define abs(x) (((x) > (0)) ? (x) : (-(x)))
#define print(x) printf("%d %d\n", x.first, x.second)
const int maxn = 1e6 + 5;
int p[maxn], a[maxn], n;
namespace Sieve {int prm[maxn >> 2], pcnt = 0; bool is[maxn];void work() {p[1] = 1;for (int i = 2; i <= n; i ++) {if (!is[i]) p[i] = i, is[prm[++ pcnt] = i] = 1;for (int j = 1; 1ll * i * prm[j] <= n && j <= pcnt; j ++) {is[i * prm[j]] = 1, p[i * prm[j]] = prm[j];if (i % prm[j] == 0) continue;}}}
}
namespace SegmentTree {struct TreeNode {ll mx, delta; int Ls, Rs;TreeNode(ll m0 = 0, ll d0 = 0, int l0 = 0, int r0 = 0) { mx = m0, delta = d0, Ls = l0, Rs = r0; }} T[maxn << 2];int ncnt = 0, root = 0;void update(int rt) { T[rt].mx = T[rt].delta + max(T[T[rt].Ls].mx, T[T[rt].Rs].mx); }#define lson l, mid, T[rt].Ls#define rson mid + 1, r, T[rt].Rsvoid modify(int l, int r, int &rt, int k, int nowl, int nowr) {if (rt == 0) rt = ++ ncnt;if (nowl <= l && r <= nowr) return T[rt].delta += k, T[rt].mx += k, void(0);int mid = (l + r) >> 1;if (nowl <= mid) modify(lson, k, nowl, nowr);if (mid < nowr) modify(rson, k, nowl, nowr);update(rt);}
} using namespace SegmentTree;
int main() {scanf("%d", &n), Sieve :: work();for (int i = 1; i <= n; i ++) {scanf("%d", &a[i]); if (i == 1) continue;if (i % 2 == 0) modify(1, 2e6, root, a[i], i, i);else modify(1, 2e6, root, a[i], i - p[i] + 1, i + p[i] - 1);} // cout << T[root].Ls << ' ' << T[root].Rs << '\n';printf("%lld\n", a[1] + T[root].mx);return 0;
}