码迷,mamicode.com
首页 > 其他好文 > 详细

AGC006C & CF1110E

时间:2020-06-27 19:55:01      阅读:81      评论:0      收藏:0      [点我收藏+]

标签:代码   复杂   改变   还原   std   printf   def   分数   个数   

这两个题都有一个公用的小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\) 。考虑这样操作对差分数组的改变:

\[\begin{aligned} &a_1,a_2,a_3,a_4\\Rightarrow&a_1,a_2-a_1,a_3-a_2,a_4-a_3\&a_1,a_2,a_2+a_4-a_3,a_4\\Rightarrow&a_1,a_2-a_1,a_4-a_3,a_3-a_2 \end{aligned} \]

发现对 \(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\) 相等。

然后再看两个数组的差分数组是否可以通过交换若干次变为相同的。没了。

总结

没啥。被上一道题基本覆盖。。。

AGC006C & CF1110E

标签:代码   复杂   改变   还原   std   printf   def   分数   个数   

原文地址:https://www.cnblogs.com/skiceanAKacniu/p/13199435.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!