【做题】agc006C - Rabbit Exercise——模型转换

原文链接https://www.cnblogs.com/cly-none/p/9745177.html

题意:数轴上有\(n\)个点,从\(1\)\(n\)编号。有\(m\)个操作,每次操作给出一个编号\(i \, 1 < i < n\),即把点\(i\)等概率移动到它关于点\(i-1\)的对称点或关于点\(i+1\)的对称点。记顺序执行这\(m\)个操作为完成1次。问完成\(k\)次后,所有点的坐标的期望值是多少。

\(n, m \leq 10^5, \, k \leq 10^{18}\)

首先,容易得到一个坐标为x的点,关于坐标为y的点对称后,新点的坐标为2y - x。我们记点i的坐标为\(p_i\),那么对它操作后得到的新点坐标的期望值就是\(\frac {2p_{i+1} + 2p_{i-1} -2p_i} {2} = p_{i+1} + p_{i-1} - p_i\)

因为期望有线性性,所以我们能确信,每一次操作就是把点\(i\)的坐标变为\(p_{i+1} + p_{i-1} - p_i\),最终答案就是每个点的坐标。

但我们还是难以解决这个问题。考虑这个性质:

\[\begin{eqnarray*} p_{i+1} - (p_{i+1} + p_{i-1} - p_i) &=& p_i - p_{i-1}\\ (p_{i+1} + p_{i-1} - p_i) - p_{i-1} &=& p_{i+1} - p_i \end{eqnarray*} \]

我们定义\(p'_i = p_i - p_{i-1}\),那么,我们发现一次操作就是交换了\(p'_i\)\(p'_{i-1}\)。因此,这\(m\)个操作就是对\(p'\)做一个置换。我们把每个环抠出来就能得到重复做\(k\)次置换之后的结果。最好再通过\(p'\)还原出\(p\)就好了。

时间复杂度\(O(n)\)

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
typedef long long ll;
int n,m,p[N],per[N],vis[N],ans[N],lop[N],cnt;
ll k,val[N],res[N];
int main() {
  scanf("%d",&n);
  for (int i = 1 ; i <= n ; ++ i)
    scanf("%lld",&val[i]);
  scanf("%d%lld",&m,&k);
  for (int i = 1 ; i <= m ; ++ i)
    scanf("%d",&p[i]);
  for (int i = n ; i >= 1 ; -- i)
    val[i] = val[i] - val[i-1];
  for (int i = 1 ; i <= n ; ++ i)
    per[i] = i;
  for (int i = 1 ; i <= m ; ++ i)
    swap(per[p[i]],per[p[i]+1]);
  for (int i = 1 ; i <= n ; ++ i) {
    if (vis[i]) continue;
    cnt = 0;
    lop[++cnt] = i;
    int pos = per[i];
    while (pos != i) {
      vis[pos] = 1;
      lop[++cnt] = pos;
      pos = per[pos];
    }
    for (int j = 1 ; j <= cnt ; ++ j)
      ans[lop[j]] = lop[(j + k - 1) % cnt + 1];
  }
  for (int i = 1 ; i <= n ; ++ i)
    res[i] = val[ans[i]];
  for (int i = 1 ; i <= n ; ++ i)
    res[i] += res[i-1];
  for (int i = 1 ; i <= n ; ++ i)
    printf("%lld.0\n",res[i]);
  return 0;
}

小结:这个特殊性质还是挺难找的。只能说找规律时,考虑差分、前缀和的变化是有用的。

posted @ 2018-10-05 16:40  莫名其妙的aaa  阅读(240)  评论(0编辑  收藏  举报