1. 程式人生 > >【做題】agc006C - Rabbit Exercise——模型轉換

【做題】agc006C - Rabbit Exercise——模型轉換

block arr 個性 特殊 但我 pla 所有 時間 %d

題意:數軸上有\(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;
}


小結:這個特殊性質還是挺難找的。只能說找規律時,考慮差分、前綴和的變化是有用的。

【做題】agc006C - Rabbit Exercise——模型轉換