zoukankan      html  css  js  c++  java
  • 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^3log_2k))

    考虑根据转移方程特点优化 dp 。发现每次 (f_i) 会变为 (f_{i-1}+f_{i+1}-f_i) 。这就相当于一个序列,每次选择一个位置 (i)(a_i) 变为 (a_{i-1}+a_{i+1}-a_i) 。考虑这样操作对差分数组的改变:

    [egin{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 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) 相等。

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

    总结

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

  • 相关阅读:
    【Spring】IOC核心源码学习(二):容器初始化过程
    啃啃老菜:Spring IOC核心源码学习(一)
    快速理解Kafka分布式消息队列框架
    浅谈分布式缓存那些事儿
    JVM调优总结
    唉,程序员要是自学能力不行就等死吧!
    游戏开发入门
    JVM源码分析-Java运行
    Java阻塞队列的实现
    Java中堆内存和栈内存详解
  • 原文地址:https://www.cnblogs.com/skiceanAKacniu/p/13199435.html
Copyright © 2011-2022 走看看