时限 300ms,非常卡常,开氧气优化反向加速,连 T 5个点。
显然有莫队 + 权值树状数组的做法,复杂度为 ,时限再开20倍就有希望卡过去了
二次离线莫队的做法:
设上一次维护的区间是
,当前询问的区间是
,当移动右端点 r 时(此时左端点不变),贡献的改变量为
中 权值 比
小的数字的个数(普通莫队用树状数组可以在线求
中权值比
小的数字的个数),注意这个贡献可以通过差分得到:用
就得到了这个答案。 而
这部分可以
预处理。
对于 :将这一次询问的移动存到 下标为 的容器中,然后用类似扫描线的做法:从 扫到 ,分别处理这些询问,边维护边处理。
莫队大约有 次移动,二次离线后就有 次询问,但只有 次维护,需要一个 查询, 更新的数据结构:权值分块
其次,
个操作全部存入 空间复杂度也达到
,会爆空间:
当 右端点 r 移动时,左端点 l 并没有移动,可以直接将移动的区间
存入到 左端点容器中,处理时遍历这个区间即可,空间复杂度降为
关于左端点的处理:和右端点的处理方法类似,先移动右端点之后,右端点固定 再移动左端点,讨论方法相同。
由于二次离线 ans[i] 存的是上一次操作和这一次操作的贡献改变量,要得到真正的答案需要将 ans[i] 按照莫队处理的顺序求前缀和
对于权值分块:原先写的是 维护块内前缀和 和 块间前缀和,但这样维护常数相对大一点 更优的做法是直接用分块维护前缀和数组(后缀和同理),对于每一次单点更新,需要修改区间 的数值,对整块打标记,零散块暴力修改即可,复杂度为 ,但代码更短更好些。
所谓的二次离线,第一次离线以 的复杂度处理所有区间询问,在莫队处理询问的过程中变成存储所有操作,第二次离线则是扫描线 从1 到 n 边扫描边处理
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10;
#define pii pair<int,int>
#define fir first
#define sec second
typedef long long ll;
#define lowbit(i) (i & (-i))
int n,m,a[maxn],block,t[maxn],tot,tp,mx[maxn],mi[maxn],sum[maxn];
inline int read() {
char cc = getchar(); int cn = 0, flus = 1;
while(cc < '0' || cc > '9') { if( cc == '-' ) flus = -flus; cc = getchar(); }
while(cc >= '0' && cc <= '9') cn = cn * 10 + cc - '0', cc = getchar();
return cn * flus;
}
inline void upd(int x,int v) {
for (int i = x; i <= n; i += lowbit(i))
sum[i] += v;
}
inline int qry(int x) {
int tot = 0;
for (int i = x; i; i -= lowbit(i))
tot += sum[i];
return tot;
}
struct node {
int id,l,r;
node(int id = 0,int l = 0,int r = 0) {
this -> id = id;
this -> l = l;
this -> r = r;
}
bool operator < (const node &rhs) const {
return (l / block + (l % block > 0)) == (rhs.l / block + (rhs.l % block > 0)) ? r < rhs.r : l < rhs.l;
}
}q[maxn];
struct ss{
int l,r,id,v; // t 代表是取较大值还是较小值, v 代表加还是减
ss(int l = 0,int r = 0,int id = 0,int v = 0) {
this -> l = l;
this -> r = r;
this -> id = id;
this -> v = v;
}
};
vector<ss> g[maxn],h[maxn];
struct Block { //值域分块
int siz,num,a[maxn],p[maxn],sum[1000];
void init() {
siz = sqrt(n);
num = n / siz + (n % siz > 0);
memset(p,0,sizeof p);
memset(sum,0,sizeof sum);
}
void upd_pre(int x,int v) {
int pos = x / siz + (x % siz > 0);
for (int i = x; i <= min(pos * siz,n); i++)
p[i] += v;
for (int i = pos + 1; i <= num; i++)
sum[i] += v;
}
void upd_suf(int x,int v) {
int pos = x / siz + (x % siz > 0);
for (int i = (pos - 1) * siz + 1; i <= x; i++)
p[i] += v;
for (int i = 1; i < pos; i++)
sum[i] += v;
}
int ask(int x) {
return p[x] + sum[x / siz + (x % siz > 0)];
}
int qry(int l,int r) {
if(l > r) return 0;
int lp = l / siz + (l % siz > 0);
int rp = r / siz + (r % siz > 0);
int ans = 0;
if(lp < rp) ans += sum[rp - 1] - sum[lp];
if(lp == rp) {
ans += p[r] - (l == (lp - 1) * siz + 1 ? 0 : p[l - 1]);
} else {
ans += p[lp * siz] - (l == (lp - 1) * siz + 1 ? 0 : p[l - 1]);
ans += p[r];
}
return ans;
}
}B;
int curleft,curright;
ll ans[maxn];
void modify(int l,int r,int x) { //莫队进行二次离线
if (curright < r) {
g[curleft].push_back(ss(curright + 1,r,x,1));
} else {
g[curleft].push_back(ss(r + 1,curright,x,-1));
}
curright = r;
if (l < curleft) {
h[curright].push_back(ss(l,curleft - 1,x,1));
} else {
h[curright].push_back(ss(curleft,l - 1,x,-1));
}
curleft = l;
}
void solve() { //扫描
B.init();
for (int i = 1; i <= n; i++) {
for (auto it : g[i])
for (int k = it.l; k <= it.r; k++)
ans[it.id] += 1ll * (mx[k] - B.ask(a[k] + 1)) * it.v;
B.upd_suf(a[i],1);
}
B.init();
for (int i = 1; i <= n; i++) {
B.upd_pre(a[i],1);
for (auto it : h[i])
for (int k = it.l; k <= it.r; k++)
ans[it.id] += 1ll * (B.ask(a[k] - 1) - mi[k]) * it.v;
}
for (int i = 1; i <= m; i++)
ans[q[i].id] += ans[q[i - 1].id];
}
int main() {
n = read(); m = read();
block = sqrt(n);
for (int i = 1; i <= n; i++) {
a[i] = read();
t[i] = a[i];
}
sort(t + 1,t + n + 1);
tp = unique(t + 1,t + n + 1) - t - 1;
for (int i = 1; i <= n; i++)
a[i] = lower_bound(t + 1,t + tp + 1,a[i]) - t;
for (int i = 1; i <= n; i++) {
mx[i] = i - qry(a[i]) - 1;
mi[i] = qry(a[i] - 1);
upd(a[i],1);
}
for (int i = 1,x,y; i <= m; i++) {
x = read(); y = read();
q[i] = node(i,x,y);
}
sort(q + 1,q + m + 1);
curleft = 1; curright = 0;
for (int i = 1; i <= m; i++)
modify(q[i].l,q[i].r,q[i].id);
solve();
for (int i = 1; i <= m; i++)
printf("%lld\n",ans[i]);
return 0;
}
/*
16 6
1 3 5 2 4 7 6 8 10 9 12 11 13 16 15 14
1 2
2 9
3 7
8 16
7 14
1 16
*/