专栏文章
动态逆序对
P3759题解参与者 2已保存评论 3
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 3 条
- 当前快照
- 1 份
- 快照标识符
- @mip92880
- 此快照首次捕获于
- 2025/12/03 08:09 3 个月前
- 此快照最后确认于
- 2025/12/03 08:09 3 个月前
题解怎么都没有二维树状数组维护的啊。CDQ,树套树,还什么线段树,分块,都太难了,我是刚学 OI 的萌新。但是我会使用二维树状数组蛮力维护动态逆序对。
每次交换两个点的贡献可以拆为两个点之间的贡献,中间且权在它们中间的值于它们的贡献,中间且权小于它们的值于它们的贡献,中间且权大于它们的值于它们的贡献。
于是我们的问题转化为:单点修改,求一个区间内的值在一个区间内的元素个数,与元素权值和。
这东西就很树状数组,写一个二维树状数组是维护动态逆序对的最好方法。注意可能卡常,因此推荐在码的过程中就注意减少计算量。手写哈希加人工
inline 可以让树状数组常数很小,虽然距离最优解很远,但是也能跑进最优解第一页。代码不短。
CPP#include <set>
#include <cstring>
#include <iostream>
#include <map>
#define lowbit(x) ((x)&-(x))
using namespace std;
using u32 = unsigned int;
using u64 = unsigned long long;
u32 n, T, u, v, a[100005], ans1;
u64 ans, val[100005], sumval[100005], ans2;
#define P 12108863
#define A 4376135492512386ull
#define B 295623ull
u32 pool1[(size_t)(P * 1.2)];
u64 pool2[(size_t)(P * 1.2)];
u64 X[(size_t)(P * 1.2)];
u32 bit1[100005];
u64 bit2[100005];
inline u64 query2(u32 x) {
u64 ans = 0;
while (x) {
ans += bit2[x];
x ^= lowbit(x);
}
return ans;
}
inline void update2(u32 x, u64 k) {
while (x <= n) {
bit2[x] += k;
x += lowbit(x);
}
}
inline void query(u32 x, u32 y) {
// i<=x, j<=y
ans1 = 0;
ans2 = 0;
while (x) {
for (u32 i = y; i; i ^= lowbit(i)) {
u32 k = (x * A ^ i * B) % P;
u64 k2 = x ^ u64(i) << 20;
while (X[k] && (X[k] ^ k2)) {
k++;
}
ans1 += pool1[k];
ans2 += pool2[k];
}
x ^= lowbit(x);
}
}
inline void update(u32 x, u32 y, u32 K1, u64 K2) {
// i<=x, j<=y, +=K1, +=K2
while (x <= n) {
for (u32 i = y; i <= n; i += lowbit(i)) {
u32 k = (x * A ^ i * B) % P;
u64 k2 = x ^ u64(i) << 20;
while (X[k] && (X[k] ^ k2)) {
k++;
}
X[k] = k2;
pool1[k] += K1;
pool2[k] += K2;
}
x += lowbit(x);
}
}
signed main() {
scanf("%u%u", &n, &T);
for (u32 i = 1; i <= n; i++) {
scanf("%u%llu", a + i, val + i);
sumval[i] = sumval[i - 1] + val[i];
}
for (u32 i = 1; i <= n; i++) {
u32 invcnt = i - 1;
ans += sumval[i - 1];
for (u32 j = a[i]; j; j ^= lowbit(j)) {
invcnt -= bit1[j];
ans -= bit2[j];
}
ans += val[i] * invcnt;
for (u32 j = a[i]; j <= n; j += lowbit(j)) {
bit1[j]++;
bit2[j] += val[i];
}
update(i, a[i], 1, val[i]);
}
memset(bit2 + 1, 0, sizeof(bit2[0]) * n);
for (u32 i = 1; i <= n; i++)
{
bit2[i] += val[i];
bit2[i + lowbit(i)] += bit2[i];
}
while (T--) {
scanf("%u%u", &u, &v);
if (u > v) {
swap(u, v);
}
if (u == v) {
printf("%llu\n", ans % 1000000007ull);
continue;
}
if (a[u] < a[v]) {
ans += val[u] + val[v];
} else {
ans -= val[u] + val[v];
}
if (u + 1 == v) {
update(u, a[u], -1, -val[u]);
update(v, a[v], -1, -val[v]);
update2(u, -val[u]);
update2(v, -val[v]);
swap(a[u], a[v]);
swap(val[u], val[v]);
update(u, a[u], 1, val[u]);
update(v, a[v], 1, val[v]);
update2(u, val[u]);
update2(v, val[v]);
printf("%llu\n", ans % 1000000007ull);
continue;
}
u32 tot_cnt = v - u - 1;
u64 tot_sum = query2(v - 1) - query2(u);
update(u, a[u], -1, -val[u]);
update(v, a[v], -1, -val[v]);
update2(u, -val[u]);
update2(v, -val[v]);
query(v, max(a[u], a[v]));
u32 vmax_cnt = ans1;
u64 vmax_sum = ans2;
query(v, min(a[u], a[v]));
u32 vmin_cnt = ans1;
u64 vmin_sum = ans2;
query(u, max(a[u], a[v]));
u32 umax_cnt = ans1;
u64 umax_sum = ans2;
query(u, min(a[u], a[v]));
u32 umin_cnt = ans1;
u64 umin_sum = ans2;
u32 up_cnt = tot_cnt - vmax_cnt + umax_cnt;
u64 up_sum = tot_sum - vmax_sum + umax_sum;
u32 down_cnt = vmin_cnt - umin_cnt;
u64 down_sum = vmin_sum - umin_sum;
u32 mid_cnt = tot_cnt - up_cnt - down_cnt;
u64 mid_sum = tot_sum - up_sum - down_sum;
if (val[u] > val[v]) {
ans += (val[u] - val[v]) * up_cnt;
ans -= (val[u] - val[v]) * down_cnt;
} else {
ans -= (val[v] - val[u]) * up_cnt;
ans += (val[v] - val[u]) * down_cnt;
}
if (a[u] < a[v]) {
ans += mid_sum << 1;
ans += mid_cnt * (val[u] + val[v]);
} else {
ans -= mid_sum << 1;
ans -= mid_cnt * (val[u] + val[v]);
}
swap(a[u], a[v]);
swap(val[u], val[v]);
update(u, a[u], 1, val[u]);
update(v, a[v], 1, val[v]);
update2(u, val[u]);
update2(v, val[v]);
printf("%llu\n", ans % 1000000007ull);
}
return 0;
}
(不理解为啥要求取模,答案严格在
long long 内,我没取模与空气斗智斗勇半天)相关推荐
评论
共 3 条评论,欢迎与作者交流。
正在加载评论...