【做题】agc006C

原文链接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),最终答案就是每个点的坐标。

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

[egin{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
",res[i]);
  return 0;
}

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

原文地址:https://www.cnblogs.com/cly-none/p/9745177.html