CodeForces - 1343D Constant Palindrome Sum(差分、暴力)
题目大意:
给出长度为\(n\)的数列\(a_i\)和\(k\),保证\(a_i \leq k\)。将\(a\)中任意一个数\(a_i\)改成\([1, k]\)中的一个数称为一次操作。问最少经过多少次操作后,\(x = a_i+a_{n-i+1} (i \leq \frac{n}{2})\)全部相等。
思路:
首先考虑暴力枚举\(x\),容易得到一个\(O(nk)\)的算法,时间瓶颈在对\(x\)的枚举之上,我们考虑怎么优化。
手玩一下即可发现,对于每一对\((i, n - i + 1)\),至多进行两次修改。我们假设\(x\)的操作次数为\(cnt[x]\),对于每一对\((i, n - i + 1)\),具体的:
- 如果\(a[i] + a[n - i + 1] == x\),那么\(cnt[x] += 0\)
- 如果\(max(a[i], a[n - i + 1]) + 1 \leq x\) && \(min(a[i], a[n - i + 1]) + k \geq x\),除掉等于\(x\)的情况\(cnt[x] += 1\)
- ELSE, \(cnt[x] += 2\)
不难发现,对于操作次数为\(1\)的pair是连续的一段区间,可以想到使用差分\(O(1)\)的修改这段区间\(x\)的操作次数,最后\(O(k)\)的更新答案。
具体实现上:
\(cnt1[x]\)表示选用\(x\)作为每对pair的结果时,修改一次的pair数量。
差分的每次修改都相当于对\([L:]\)区间进行修改。
Code:
ll n, k, a[N], cnt0[N], cnt1[N];
int main() {
ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int T; cin >> T;
while (T--) {
//init
ll ans = INF;
cin >> n >> k;
for (int i = 1; i <= (k << 1); i++)
cnt0[i] = cnt1[i] = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= n / 2; i++) {
cnt0[a[i] + a[n - i + 1]]++;
cnt1[min(a[i], a[n - i + 1]) + 1]++, cnt1[max(a[i], a[n - i + 1]) + k + 1]--;
cnt1[a[i] + a[n - i + 1]]--, cnt1[a[i] + a[n - i + 1] + 1]++; //去掉修改次数为0
}
for (int i = 2; i <= (k << 1); i++) { //枚举答案
cnt1[i] += cnt1[i - 1];
ckmin(ans, cnt1[i] + 2ll * (n / 2 - cnt0[i] - cnt1[i]));
}
cout << ans << "\n";
}
return 0;
}