ABC393D题解
大概评级:黄。
签到题。
显然,我们需要找到所有的 \(1\) 的位置,然后看其它的 \(1\) 靠拢过来需要几次操作,然后对于每个位置的答案取个最小值就好了。
直接暴力肯定是不行的,考虑优化计算其它的 \(1\) 靠拢过来需要几次操作的过程,首先为了方便,我们将当前位置设为 \(i\),将 \(i\) 左边的 \(1\) 的位置组成的集合称为 \(a\),将 \(i\) 右边的 \(1\) 的位置组成的集合称为 \(b\),集合 \(a\) 的大小称为 \(as\),集合 \(b\) 的大小称为 \(bs\),那么集合 \(a\) 的贡献就是:
\[\sum_{j = 1}^{as} i-a_j-(as-j)
\]
\[= \sum_{j = 1}^{as} a_j-as \times i-\sum_{j = 1}^{as-1} j
\]
至于一开始的式子为什么还要减去 \(as-j\) 是因为每次 \(i\) 左边的一个 \(1\) 靠拢过来后,下一个 \(1\) 靠拢过来需要的次数就会少 \(1\)。
继续正题,我们发现这个式子化简后是可以使用两个前缀和维护的。
额……其实集合 \(b\) 贡献也是差不多的:
\[\sum_{j = 1}^{bs} b_j-i-(bs-j)
\]
\[= bs \times i-\sum_{j = 1}^{bs} b_j-\sum_{j = 1}^{bs-1} j
\]
可以用两个后缀和维护。
十年 OI 一场空,不开 long long 见祖宗。
总时间复杂度:\(O(N+N) = O(N)\)。
代码:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5e5+5;
char a[N];
int sum1[N];
int sum2[N];
signed main()
{
int n;
scanf("%lld",&n);
scanf("%s",a+1);
for(int i = n;i;i--)
{
sum1[i] = sum1[i+1]+(a[i] == '1');
sum2[i] = sum2[i+1]+(a[i] == '1')*i;
}
int minn = 1e18;
int pre1 = 0,pre2 = 0;
for(int i = 1;i<=n;i++)
{
pre1+=(a[i] == '1');
pre2+=(a[i] == '1')*i;
if(a[i] == '1')
{
minn = min(minn,(pre1-1)*i-(pre2-i)-(pre1-1)-(pre1-1)*(pre1-2)/2+(sum2[i]-i)-(sum1[i]-1)*i-(sum1[i]-1)-(sum1[i]-1)*(sum1[i]-2)/2);
}
}
printf("%lld",minn);
return 0;
}

浙公网安备 33010602011771号