n個椅子,c種顏色排成一圈,間隔1m。隨機選一種顏色,你要馬上移動到這種顏色的椅子上(原本顏色相同則不動)。求走動距離的最小期望(輸出最簡分數)。
1 ≤ c ≤ n ≤ 1e6
據說暴力模擬+優化(n^2—>nlogn?)2.5s內可過……不過這裏用了一些數學知識,複雜度降到O(n)。(其實是2n,不過eoj評測姬太快了,可以忽略)
首先肯定是常規的環拆鏈操作:複製一份放到後面去。這樣就可以規定正方向爲向右,從左往右掃描了。
要求出答案,關鍵在於求出要坐的這個位置。要求出這個位置,無疑需要求出位置 i 到各個顏色椅子的最短期望距離和 。
設 爲椅子 i 到顏色爲 k 的椅子的最短期望距離。即:
我們發現,對每一個k, 是一個關於 i 的分段函數:
- i 在距離最近的顏色爲k的椅子左邊,則 i 每右移一次,離該椅子的距離-1,此時 ;
- 同理,i 在距離最近的顏色爲k的椅子右邊,則 i 每右移一次,離該椅子的距離+1,此時 ;
- 於是在中間某個時刻,我們移動到了這張椅子上,此時 ,這裏是函數的駐點。再求(僞)二階導,由於一階導在這個點從-1變成了+1,我們可以認爲(僞)二階導 。(這樣設定二階導是爲了方便後面求一階導和答案)
再考慮兩個相鄰的同色(k)椅子:當經過兩者中點前,我們離左邊椅子的距離小於離右邊椅子的距離,反之亦然。也就是說,在經過兩者中點時, 由+1變爲了-1(中間的椅子數爲奇時,會在中點處變爲0)。
因此,需要對ceil((i+j)/2)和floor((i+j)/2)這兩個點(中間的椅子數爲奇時,一個點)的二階導分別減1。
綜上,二階導處理完畢。對二階導求前綴和並且每項減c,得到一階導。再對一階導求前綴和(注意特判,第0個位置就是-c),得到每個位置的 ,最後取最小值,這題就終於做完了……
#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);
}