AT2164 AGC006C Rabbit Exercise
数轴上有 (n) 个点,每个点的坐标为 (a_i) 。一轮操作包含 (m) 次变换,第 (i) 次将 (b_i(1<b_i<n)) 随机跳到点关于 (b_i-1) 或 (b_i+1) 的对应点。求 (k) 轮操作后每个点的期望位置
(n, mleq10^5, kleq10^{18}, |a_i|leq10^9)
期望,差分,倍增
假设点 (a_i) 变换后坐标为 (x) ,则 (frac{a_i+x}{2}=a_{i-1}) 或 (a_{i+1}) ,即 (x=2a_{i-1}-a_i) 或 (2a_{i+1}-a_i) ,两式相加得 (x=a_{i-1}+a_{i+1}-a_i) ,即为变换后坐标期望值。
考虑类似题目 CF1110E Magic Stones 的做法,将原序列差分得到 (c_i) ,可以发现对 (a_i) 的变换相当于交换了 (c_i, c_{i+1})
可以发现 (k) 轮操作相对顺序不影响答案,由于变换两次相当于变换一次后再变换一次,变换四次相当于变换两次后再变换两次,于是可以考虑倍增
可以 (O(n)) 求出一轮操作后差分数组的排列,倍增的预处理可以用排列完成
时间复杂度 (O(klog n))
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
ll k, a[maxn];
int n, m, data[maxn], pos[61][maxn];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld", a + i), data[i] = i;
}
scanf("%d %lld", &m, &k);
for (int i = 1, x; i <= m; i++) {
scanf("%d", &x);
swap(data[x], data[x + 1]);
}
for (int i = 1; i <= n; i++) {
pos[0][i] = data[i];
}
for (int i = 1; i < 61; i++) {
for (int j = 1; j <= n; j++) {
pos[i][j] = pos[i - 1][pos[i - 1][j]];
}
}
for (int i = 1; i <= n; i++) {
data[i] = i;
}
for (int p = 60; ~p; p--) {
if (k >> p & 1) {
for (int i = 1; i <= n; i++) {
data[i] = pos[p][data[i]];
}
}
}
static int c[maxn];
for (int i = 1; i <= n; i++) {
c[i] = a[i] - a[i - 1];
}
for (int i = 1; i <= n; i++) {
a[i] = a[i - 1] + c[data[i]];
printf("%lld
", a[i]);
}
return 0;
}