AtCoder Beginner Contest 367
title: AtCoder Beginner Contest 367
categories:
- ICPC
tags:
- null
abbrlink: 9364694a
date: 2024-11-17 00:00:00
AtCoder Beginner Contest 367
待补:ABC 367 G 题解 - spdarkle - 博客园 (cnblogs.com)
D.一个湖周围有 $N$ 个休息区。
这些休息区按顺时针顺序编号为 $1$ 、 $2$ 、……、 $N$ 。
从休息区 $i$ 顺时针走到休息区 $i+1$ 需要 $A_i$ 步。(其中休息区 $N+1$ 指的是休息区 $1$ )。
从休息区 $s$ 顺时针走到休息区 $t$ ( $s \neq t$ )所需的最小步数是 $M$ 的倍数。
求 $(s,t)$ 的可能对数。
Sol:注意只能顺时针,所以固定起点的时候,到达任何一个点的路程都是确定的。考虑破环成链,维护%mod意义下的前缀和一个map桶,像滑动窗口维护这个map。但要注意边界,因$a_{i}$表示的是一条边。
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = n + 1; i <= 2 * n; i++) a[i] = a[i - n];
map<int, int> mp;
for (int i = 1; i <= 2 * n; i++) a[i] += a[i - 1], a[i] %= m;
int ans = 0;
// int sum = 0;
//for (int i = 0; i <= 2 * n; i++) cerr << a[i] << " ";
// cerr << endl;
// deb(0);
// for (int i = 0; i <= n - 1; i++) cerr << a[i] << " ";
//cerr << endl;
for (int i = 0; i <= n - 1; i++) mp[a[i]]++;
ans = mp[0]-1;
//deb(ans,mp);
for (int i = 1; i <= n - 1; i++) {
//deb(i);
//for (int j = i; j <= i + n - 1; j++) cerr << a[j] << " ";
//cerr << endl;
mp[a[i - 1]]--;
if (mp[a[i - 1]] == 0)
mp.erase(a[i - 1]);
mp[a[i + n - 1]]++;
ans += mp[a[i]]-1;
//deb(ans,mp);
}
cout << ans << endl;
}
E:基环树倍增模板:
给你一个长度为 $N$ 的序列 $X$ ,其中每个元素都介于 $1$ 和 $N$ 之间(含),以及一个长度为 $N$ 的序列 $A$ 。
打印在 $A$ 上执行以下操作 $K$ 次的结果。
- 用 $B$ 替换 $A$ ,使得 $B_i = A_{X_i}$ .
Sol:发现可以建图i向$x_{i}$连边。然后形成了内向基环树森林。线性dp做法的实现难度较大,倍增对于在环上走很多步非常有用。
int n, m;
int a[N];
int x[N];
int st[N][65];
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> x[i];
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) st[i][0] = x[i];
for (int j = 1; j <= 60; j++) {
for (int i = 1; i <= n; i++) {
st[i][j] = st[st[i][j - 1]][j - 1];
}
}
for (int i = 1; i <= n; i++) {
int pos = i;
for (int j = 60; j >= 0; j--) {
if ((m >> j) & 1)
pos = st[pos][j];
}
cout << a[pos] << " ";
}
}
F.
- 序列哈希板子:考虑对于一个序列,如果我们发现每次关心的都是其排序后结果,这意味着我们不在乎出现位置。我们考虑维护值域大小为M的桶(string代替),如果是集合那么其实是个01字符串。如果是可重集合,那么字符串第i个位置有cnt[i],表示这个可重集合中i这个值出现了cnt[i]次。选择一个质数base,记第i个位置的哈希值贡献为$base \ ^{cnt[i]}$.
题意:给你长度为 $N$ 的正整数序列: $A=(A_1,A_2,\ldots,A_N)$ 和 $B=(B_1,B_2,\ldots,B_N)$ .
您需要依次处理 $Q$ 个查询。 $i$ -th 查询的解释如下。
- 给定正整数 $l_i,r_i,L_i,R_i$ 。如果可以重新排列子序列 $(A_{l_i},A_{l_i+1},\ldots,A_{r_i})$ 以匹配子序列 $(B_{L_i},B_{L_i+1},\ldots,B_{R_i})$ ,则打印 “是”,否则打印 “否”。
constexpr u64 P = u64(1E18) + 9;
//constexpr u64 P = 998244353;
using Z = ModInt64<P>;
int n, m;
int a[N];
int b[N];
Z hsa[N], hsb[N];
random_device rd;
mt19937_64 gen(rd());
static int findprime() {
int n = gen() % 1000000 + 1e6;
if (n % 2 == 0)
n++;
while (true) {
bool ok = 1;
for (int i = 3; i * i <= n; i += 2) {
if (n % i == 0) {
ok = 0;
n += 2;
break;
}
}
if (ok)
return n;
}
}
Z pwn[N];
const ull base = findprime();
void solve() {
cin >> n >> m;
pwn[0] = (u64)1;
for (int i = 1; i <= N - 2; i++) pwn[i] = (pwn[i - 1] * base);
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= n; i++) {
hsa[i] = hsa[i - 1] + pwn[a[i]];
hsb[i] = hsb[i - 1] + pwn[b[i]];
}
for (int i = 1; i <= m; i++) {
int l1, l2, r1, r2;
cin >> l1 >> r1 >> l2 >> r2;
if (hsa[r1] - hsa[l1 - 1] == hsb[r2] - hsb[l2 - 1])
cout << "Yes" << endl;
else
cout << "No" << endl;
}
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 爱飞鱼的blog!