AGC006C & CF1110E
这两个题都有一个公用的小trick
,所以我就写一起了!
AGC 006 C
题目叙述
一些兔子站在坐标轴上,兔子的坐标为 \(x_1,x_2,\cdots ,x_n\) 。第 \(i\) 只兔子会这样跳跃:随机等概率选择相邻两个兔子之一,以那只兔子为中心,跳到对称的另一边。现在定义一组跳跃为让编号为 \(a_1,a_2,\cdots ,a_n\) 的这些兔子跳跃。求 \(k\) 次跳跃后每只兔子期望位置。
题解
首先有一个显而易见的 dp 。设 \(f_i\) 为第 \(i\) 只兔子目前位置的期望,每次按顺序执行跳跃,转移即可。复杂度 \(\mathcal O(mk)\) 。
然后在看到 \(k \le 10^{18}\),可以想到矩阵乘法优化 dp。但复杂度为 \(\mathcal O(n^3\log_2k)\)。
考虑根据转移方程特点优化 dp 。发现每次 \(f_i\) 会变为 \(f_{i-1}+f_{i+1}-f_i\) 。这就相当于一个序列,每次选择一个位置 \(i\) 将 \(a_i\) 变为 \(a_{i-1}+a_{i+1}-a_i\) 。考虑这样操作对差分数组的改变:
发现对 \(i\) 这个位置操作相当于交换 \(i\) 和 \(i+1\) 的差分数组。
所以每一次操作相当于交换差分数组的两个位置。所以每一轮操作就是交换若干对数,不难发现这其实是若干个环的结构。也就类似于置换。那么每次求出一个数交换的环有多大,然后得到最终状态并通过差分数组还原原数组即可。
总结
- dp 可以针对转移方程优化
- \(a_i\rightarrow a_{i-1}+a_{i+1}-a_i\) 这种可以考虑研究他的差分数组。
代码
#include <cstdio>
#include <vector>
using namespace std;
typedef long long ll;
const int N = 1e5 + 5;
int n, m, a[N], go[N], bg[N], ps[N];
ll k, chaf[N], ans[N], x[N];
bool vis[N];
vector<int> cir[N];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%lld", &x[i]);
for (int i = 1; i <= n; ++i) chaf[i] = x[i] - x[i - 1];
scanf("%d%lld", &m, &k);
for (int i = 1; i <= m; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) go[i] = i;
for (int i = 1; i <= m; ++i) swap(go[a[i]], go[a[i] + 1]);
for (int i = 1; i <= n; ++i) {
if (vis[i]) continue ;
int u = i;
while (!vis[u]) {
vis[u] = 1;
bg[u] = i;
u = go[u];
}
u = i;
bg[u] = i;
cir[i].push_back(u);
ps[u] = 0;
while (go[u] != i) {
u = go[u];
ps[u] = cir[i].size();
cir[i].push_back(u);
}
}
for (int i = 1; i <= n; ++i) ans[i] = chaf[cir[bg[i]][(ps[i] + k) % cir[bg[i]].size()]];
for (int i = 1; i <= n; ++i) ans[i] += ans[i - 1];
for (int i = 1; i <= n; ++i) printf("%lld\n", ans[i]);
return 0;
}
CF1110E
题目叙述
一个数组 \(a\) 考虑对他进行如下变换:
- 每次可以选择位置 \(i\) 将第 \(i\) 个位置的数换为 \(a_{i+1}+a_{i-1}-a_{i}\)。
问是否能将 \(a\) 数组通过若干次变换变为 \(t\) 数组(\(a\) 和 \(t\) 都给定)。
题解
首先发现 \(a_1\) 和 \(a_n\) 不可进行这种变换,所以 \(a_1\) 必然和 \(t_1\) 相等并且 \(a_n\) 和 \(t_n\) 相等。
然后再看两个数组的差分数组是否可以通过交换若干次变为相同的。没了。
总结
没啥。被上一道题基本覆盖。。。