这两个题都有一个公用的小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^3log_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 ightarrow 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
", 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) 相等。
然后再看两个数组的差分数组是否可以通过交换若干次变为相同的。没了。
总结
没啥。被上一道题基本覆盖。。。