n个椅子,c种颜色排成一圈,间隔1m。随机选一种颜色,你要马上移动到这种颜色的椅子上(原本颜色相同则不动)。求走动距离的最小期望(输出最简分数)。
1 ≤ c ≤ n ≤ 1e6
据说暴力模拟+优化(n^2—>nlogn?)2.5s内可过……不过这里用了一些数学知识,复杂度降到O(n)。(其实是2n,不过eoj评测姬太快了,可以忽略)
首先肯定是常规的环拆链操作:复制一份放到后面去。这样就可以规定正方向为向右,从左往右扫描了。
要求出答案,关键在于求出要坐的这个位置。要求出这个位置,无疑需要求出位置 i 到各个颜色椅子的最短期望距离和
d(i)
d
(
i
)
。
设
D(i,k)
D
(
i
,
k
)
为椅子 i 到颜色为 k 的椅子的最短期望距离。即:
我们发现,对每一个k, D(i,k) D ( i , k ) 是一个关于 i 的分段函数:
- i 在距离最近的颜色为k的椅子左边,则 i 每右移一次,离该椅子的距离-1,此时 D′(i,k)=−1 D ′ ( i , k ) = − 1 ;
- 同理,i 在距离最近的颜色为k的椅子右边,则 i 每右移一次,离该椅子的距离+1,此时 D′(i,k)=+1 D ′ ( i , k ) = + 1 ;
- 于是在中间某个时刻,我们移动到了这张椅子上,此时 D′(i,k)=0 D ′ ( i , k ) = 0 ,这里是函数的驻点。再求(伪)二阶导,由于一阶导在这个点从-1变成了+1,我们可以认为(伪)二阶导 D′′(i,k)=+2 D ″ ( i , k ) = + 2 。(这样设定二阶导是为了方便后面求一阶导和答案)
再考虑两个相邻的同色(k)椅子:当经过两者中点前,我们离左边椅子的距离小于离右边椅子的距离,反之亦然。也就是说,在经过两者中点时,
D′(i,k)
D
′
(
i
,
k
)
由+1变为了-1(中间的椅子数为奇时,会在中点处变为0)。
因此,需要对ceil((i+j)/2)和floor((i+j)/2)这两个点(中间的椅子数为奇时,一个点)的二阶导分别减1。
综上,二阶导处理完毕。对二阶导求前缀和并且每项减c,得到一阶导。再对一阶导求前缀和(注意特判,第0个位置就是-c),得到每个位置的 d(i) 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);
}