Cow Checkups (USACO Silver 2025 January)
题目复述:给定, (), 对于所有,将反转,然后数有多少个,使得,求所有组合下的个数之和。
这是一道Prefix sum题。可以看到这个题对于复杂度要求比较高,仅仅能计算一个范围的贡献都不够。我们需要寻找对所有位置扫描一遍,然后对每个位置都能计算对最终结果的贡献的算法。
我们考虑每个的一对位置对,设为这一对数字对最终的结果的贡献。我们观察,对于固定的,随着的变化,是分段常数或分段线性的,分段点在和。
设, ,那么可以分成四种情况:
- :
- :
- :
- 如果,除以上之外,还在区间不包含时产生贡献: +=
有了这个之后,我们可以对循环,用prefix sum来计算对于这个的所有的的和。计算方法如下:
- 4的部分容易处理,对于每一个是计算的。
- 1的3的部分,可以通过从左到右扫描时,维护一个对于所有的值的的和来计算。就是下面程序中
left[x]
和right[x]
。 从0开始变大的时候,开始left
和right
逐渐增加,当大于之后,又逐渐减少。 - 2的部分,同样维护一个之间的所有值的的个数,小于时逐渐减少,大于后逐渐增多。下面的
mid[x]
。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int n;
int a[500005], b[500005];
ll left[500005]; // left[x] is the sum of j+1 for all b[j]=x, to the left of i
ll right[500005]; // right[x] is the sum of N-j for all b[j]=x, to the right of N-i
ll mid[500005];
ll ans;
int main() {
scanf("%d",&n);
for(int i=0;i<n;++i)
scanf("%d", &a[i]);
for(int i=0;i<n;++i) {
scanf("%d", &b[i]);
mid[b[i]]++;
}
for (int i = 0; i < n; i++) {
int L = min(i, n-i-1), R = max(i, n-i-1);
ans += left[a[i]] + right[a[i]];
if (a[i] == b[i])
ans += (ll)i*(i+1)/2 + (ll)(n-1-i)*(n-i)/2;
ans += mid[a[i]] * (L+1);
if (i+1 <= n-i-2) { // i and N-i-1 are added
left[b[i]] += i+1;
right[b[n-i-1]] += i+1;
mid[b[i]]--;
mid[b[n-i-1]]--;
} else if (i >= n-i-1) { // N-i-2 and i+1 is removed
left[b[n-i-2]] -= n-i-1;
right[b[i+1]] -= n-i-1;
mid[b[i+1]]++;
mid[b[n-i-2]]++;
}
}
printf("%lld\n",ans);
return 0;
}