LG7448 [Ynoi2007] rdiq 【莫队,分块】
区间本质不同逆序对计数,不强制在线。
考虑二次离线莫队,右端点移动的贡献是 \([l,r]\) 中 \(>a_r\) 的权值数量减去 \([l,\text{pre}_r]\) 中 \(>a_r\) 的权值数量,左端点同理。对这些询问按 \(l\) 从大到小的顺序依次处理,问题变为矩阵 \(\mathcal O(\sqrt n)\) 单点修改 \(\mathcal O(1)\) 查询前缀和。
为了方便维护,设 \(\{a'_i\}\) 表示将二元组序列 \(\{(a_i,i)\}\) 离散化得到的排列,考虑大小关系时将 \(a_i\) 换为 \(a_i'\) 答案不变。
将矩阵划分为 \(n\) 个 \(n^{1/2}\times n^{1/2}\) 的子矩阵,散块部分在修改的时候直接贡献到询问,这样的询问只有 \(\mathcal O(n^{1/2})\) 个,从而将矩阵缩小为 \(n^{1/2}\times n^{1/2}\) 的规模,再次分块即可。
时间复杂度 \(\mathcal O(n(\sqrt n+\sqrt m))\),空间复杂度 \(\mathcal O(n+m)\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 100000, M = 500000, B0 = 18, B1 = B0 * B0, B2 = B0 * B1;
int n, a[N], b[N], cnt[N], tmp[N], lst[N], id[N], m, B;
struct Node {
int l, r, id, _;
Node(int _1 = 0, int _2 = 0, int _3 = 0, int _4 = 0): l(_1), r(_2), id(_3), _(_4){}
bool operator < (const Node &o) const {
if(_ != o._) return _ < o._;
return (_ & 1) ^ (r < o.r);
}
} cst[M];
LL ans[M], mzk[M];
int s0[B0][B0], s1[B1][B0], s2[B0][B1], s3[B1][B1], s4[N];
void ins(int x, int v){
int y = b[x];
for(int i = x / B2 + 1;i < B0;++ i)
for(int j = y / B2 + 1;j < B0;++ j) s0[i][j] += v;
for(int i = x / B1 + 1, i2 = (x / B2 + 1) * B0;i < i2;++ i)
for(int j = y / B2 + 1;j < B0;++ j) s1[i][j] += v;
for(int i = x / B2 + 1;i < B0;++ i)
for(int j = y / B1 + 1, j2 = (y / B2 + 1) * B0;j < j2;++ j) s2[i][j] += v;
for(int i = x / B1 + 1, i2 = (x / B2 + 1) * B0;i < i2;++ i)
for(int j = y / B1 + 1, j2 = (y / B2 + 1) * B0;j < j2;++ j) s3[i][j] += v;
int i2 = (x / B1 + 1) * B1;
for(int i = x + 1;i < n && i < i2;++ i) if(b[i] > y) s4[i] += v;
for(int j = y + 1, j2 = (y / B1 + 1) * B1;j < n && j < j2;++ j) if(id[j] >= i2) s4[id[j]] += v;
}
int qry(int x){int y = b[x]; return s0[x / B2][y / B2] + s1[x / B1][y / B2] + s2[x / B2][y / B1] + s3[x / B1][y / B1] + s4[x];}
void solve(const vector<Node> *tkn){
memset(cnt, 0, sizeof(cnt));
memset(s0, 0, sizeof(s0));
memset(s1, 0, sizeof(s1));
memset(s2, 0, sizeof(s2));
memset(s3, 0, sizeof(s3));
memset(s4, 0, sizeof(s4));
for(int i = 0;i < n;++ i) ++ cnt[a[i]];
for(int i = 1;i < n;++ i) cnt[i] += cnt[i - 1];
for(int i = 0;i < n;++ i) id[b[i] = --cnt[a[i]]] = i;
memset(tmp, -1, sizeof(tmp));
for(int i = 0;i < n;++ i){lst[i] = tmp[a[i]]; tmp[a[i]] = i;}
memset(tmp, -1, sizeof(tmp));
for(int i = n - 1;i >= 0;-- i){
if(tmp[a[i]] >= 0) ins(tmp[a[i]], -1);
ins(tmp[a[i]] = i, 1);
for(auto [l, r, id, coe] : tkn[i]){
LL res = 0;
for(int j = l;j <= r;++ j){res += qry(j); if(lst[j] >= i) res -= qry(lst[j]);}
ans[id] += coe * res;
}
}
}
vector<Node> tkn1[N], tkn2[N];
int main(){
ios::sync_with_stdio(0);
cin >> n;
for(int i = 0;i < n;++ i){cin >> a[i]; a[i] = n - a[i];}
cin >> m; B = n / sqrt(m) + 1;
for(int i = 0;i < m;++ i){
cin >> cst[i].l >> cst[i].r;
cst[i]._ = --cst[i].l / B;
-- cst[i].r; cst[i].id = i;
}
sort(cst, cst + m);
for(int i = 0, ql = 0, qr = 0;i < m;++ i){
if(qr < cst[i].r){tkn1[ql].emplace_back(qr + 1, cst[i].r, i, 1); qr = cst[i].r;}
if(ql > cst[i].l){tkn2[n - qr - 1].emplace_back(n - ql, n - cst[i].l - 1, i, 1); ql = cst[i].l;}
if(qr > cst[i].r){tkn1[ql].emplace_back(cst[i].r + 1, qr, i, -1); qr = cst[i].r;}
if(ql < cst[i].l){tkn2[n - qr - 1].emplace_back(n - cst[i].l, n - ql - 1, i, -1); ql = cst[i].l;}
}
solve(tkn1); reverse(a, a + n);
for(int i = 0;i < n;++ i) a[i] = n - 1 - a[i];
solve(tkn2);
for(int i = 1;i < m;++ i) ans[i] += ans[i - 1];
for(int i = 0;i < m;++ i) mzk[cst[i].id] = ans[i];
for(int i = 0;i < m;++ i) printf("%lld\n", mzk[i]);
}