EOJ 2018.2.5新生訓練Week3 D.Game of Chairs

n個椅子,c種顏色排成一圈,間隔1m。隨機選一種顏色,你要馬上移動到這種顏色的椅子上(原本顏色相同則不動)。求走動距離的最小期望(輸出最簡分數)。
1 ≤ c ≤ n ≤ 1e6

據說暴力模擬+優化(n^2—>nlogn?)2.5s內可過……不過這裏用了一些數學知識,複雜度降到O(n)。(其實是2n,不過eoj評測姬太快了,可以忽略)


首先肯定是常規的環拆鏈操作:複製一份放到後面去。這樣就可以規定正方向爲向右,從左往右掃描了。
要求出答案,關鍵在於求出要坐的這個位置。要求出這個位置,無疑需要求出位置 i 到各個顏色椅子的最短期望距離和d(i)
D(i,k) 爲椅子 i 到顏色爲 k 的椅子的最短期望距離。即:

d(i)=k=1cD(i,k)

我們發現,對每一個k,D(i,k) 是一個關於 i 的分段函數:
  1. i 在距離最近的顏色爲k的椅子左邊,則 i 每右移一次,離該椅子的距離-1,此時D(i,k)=1
  2. 同理,i 在距離最近的顏色爲k的椅子右邊,則 i 每右移一次,離該椅子的距離+1,此時D(i,k)=+1
  3. 於是在中間某個時刻,我們移動到了這張椅子上,此時D(i,k)=0 ,這裏是函數的駐點。再求(僞)二階導,由於一階導在這個點從-1變成了+1,我們可以認爲(僞)二階導D(i,k)=+2 。(這樣設定二階導是爲了方便後面求一階導和答案)

再考慮兩個相鄰的同色(k)椅子:當經過兩者中點前,我們離左邊椅子的距離小於離右邊椅子的距離,反之亦然。也就是說,在經過兩者中點時,D(i,k) 由+1變爲了-1(中間的椅子數爲奇時,會在中點處變爲0)。
因此,需要對ceil((i+j)/2)和floor((i+j)/2)這兩個點(中間的椅子數爲奇時,一個點)的二階導分別減1。

綜上,二階導處理完畢。對二階導求前綴和並且每項減c,得到一階導。再對一階導求前綴和(注意特判,第0個位置就是-c),得到每個位置的d(i) ,最後取最小值,這題就終於做完了……

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 2e6+5;
int n, c, a[maxn], nxt[maxn];
ll d[maxn];

ll gcd(ll a, ll b)
{
    return b ? gcd(b, a%b) : a;
}

int main()
{
    cin >> n >> c;
    for (int i = 1; i <= n; ++i)
        scanf("%d", a+i);
    for (int i = n+1; i <= (n<<1); ++i)
        a[i] = a[i-n];
    n <<= 1;
    for (int i = n; i >= 1; --i)
    {
        d[i] += 2;
        int &j = nxt[a[i]];
        if (j)
        {
            d[(i+j)>>1] -= 1;
            d[(i+j+1)>>1] -= 1;
        }
        j = i;
    }
    for (int i = 1; i <= n; ++i)
        d[i] += d[i-1];
    d[0] = -c;
    for (int i = 1; i <= n; ++i)
        d[i] += -c;
    ll sum = 0;
    for (int i = 1; i <= n; ++i)
    {
        int &j = nxt[a[i]];
        if (j)
        {
            sum += i;
            j = 0;
        }
    }
    ll ans = (ll)n * n;
    for (int i = 1; i <= n; ++i)
    {
        sum += d[i-1];
        if (sum < ans) ans = sum;
    }
    ll g = gcd(ans, c);
    ans /= g;
    c /= g;
    printf("%lld/%d\n", ans, c);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章