做法
带log的分块相信大家都会,今天我们来讲讲不带log。
首先先离散化,这样值域就变成n了。
我们以根号为阈值分块,然后我们记A->B的含义是将A和B两个序列拼接在一起有多少逆序对两个元素一个来自A另一个来自B。
首先考虑如何求A->B,可以发现A和B的内部顺序是没有关系的,因此如果我们把A和B排好序了,运用归并即可线性求得A->B。
现在我们先把每个块排序,然后预处理一个位置到块头的逆序对个数(树状数组解决),接下来对于块内询问[l,r]假设块头和块尾分别是u和v,那么。
ans([l,r])=ans([u,r])-ans([u,l-1])-[u,l-1]->[l,r]。
其中前缀ans已经预处理,剩下那个可以归并。
我们解决块间贡献可以枚举两个块然后归并,接下来设f[l,r]表示第l到r块的答案。
f[l,r]=f[l,r-1]+f[l+1,r]-f[l+1,r-1]+l->r。
因此可以预处理块间贡献。
散块间贡献可以归并得到。
现在解决散块与块间的贡献。
可以先预处理一个位置对一个块的贡献。
可以将元素按顺序插入,维护一个块内的答案,即可预处理。
散块一定是到块头或块尾的区间,再前缀和或后缀和即可。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=50000+10,B=224;
int f[B+10][B+10],num[B+10],g[maxn][B+10];
int tree[maxn];
int a[maxn],belong[maxn],b[maxn],c[maxn],d[maxn],sum[maxn],cnt[maxn];
int i,j,k,l,r,s,t,n,m,tot,top,ans,now;
int read(){
int x=0,f=1;
char ch=getchar();
while (ch<'0'||ch>'9'){
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9'){
x=x*10+ch-'0';
ch=getchar();
}