Offline algorithm-CDQ divide and conquer
CDQ (SHY) is obviously a person's name, Chen Danqi (MM) (NOI 2008 gold medalist).
-
Start with merging (instead of reverse-order pairs, we want to introduce divide-and-conquer ideas directly instead of processing objects)
A very simple merge sort: a disorderly sequence, each time it is folded in half, similar to the line segment tree data structure, each sub-interval is processed first, and finally summarized to the upper level.
The number of layers does not exceed log(n), and the complexity of each processing is O(n), so the complexity is O(nlogn).
code:
void merge_sort(int l,int r)
{
if(l==r)return;
int mid=(l+r>>1);
merge_sort(l,mid);merge_sort(mid+1,r);
int i=l,j=mid+1,k=l;
while(i<=mid&&j<=r)
{
if(a[i]<a[j])t[k++]=a[i++];
else t[k++]=a[j++];
}
while(i<=mid)t[k++]=a[i++];
while(j<=r)t[k++]=a[j++];
go(i,l,r)a[i]=t[i];
}
-
Simple application (reverse sequence pair)
Inverse order pairs are logarithms of binary groups (i,j) satisfying I < J & & a [i] > a [j] in a sequence of numbers.
Take a chestnut (delicious): 1,4,3,8,4,3,8(val)
1,2,3,4,5,6,7(pos)
For pos:(2,3), (2,6), (4,5), (4,6), (5,6) are inverse pairs.
So as far as the nature of merger is concerned, because pos is naturally ordered, we just need to look at val.
In merge, if the current position j on the right is smaller than the position I on the left, then it must be smaller than all the numbers in [i,mid], so ans+=mid-i+1.
-
From Inverse Sequence Pairing to Two-Dimensional Partial Sequence Problem
In fact, the two-dimensional partial ordering problem is to scramble pos of the inverse pair and repeat it. It can be understood that i n the plane rectangular coordinate system, there are n points (i,j), and find out how many points are in the rectangle formed by each point and (0, 0).
Double-keyword sorting followed by direct tree arrays can be:
Examples: Two-dimensional partial order
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define lowbit(x) (x&-x)
using namespace std;
const int N=100010;
struct star
{
int x,y;
}s[N];
long long tarr[N];
int n;
long long ans;
bool cmp(star a,star b)
{
return a.x==b.x?a.y<b.y:a.x<b.x;
}
void add(int pos,int val)
{
while(pos<=N)
{
tarr[pos]+=val;
pos+=lowbit(pos);
}
}
int query(int pos)
{
int res=0;
while(pos)
{
res+=tarr[pos];
pos-=lowbit(pos);
}return res;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&s[i].x,&s[i].y);
}
sort(s+1,s+1+n,cmp);
for(int i=1;i<=n;i++)
{
ans+=query(s[i].y);
add(s[i].y,1);
}
cout<<ans;
}
-
From two-dimensional partial order to three-dimensional partial order
Board: Flowers blossom on the street
With the previous foundation, the three-dimensional partial ordering is also very simple:
In merge, if the second dimension of i on the left and j on the right satisfies (bi < bj), ci is added to the tree array until there is a bi that does not satisfy this relationship, then J can query all ci < CJ triples in the tree array because of the previous ranking of the first dimension and the return of the second dimension. Moreover, the first two dimensions must satisfy the conditions. Here's one to go heavy or quite annoying.
code:
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #define lowbit(x) (x&-x) using namespace std; const int N=100010; const int M=200010; struct node { int a,b,c,f,w; }e[N],t[N]; int cnt; int n,m; int ans[N]; int tarr[M]; bool cmp(node x,node y) { return x.a==y.a?(x.b==y.b?x.c<y.c:x.b<y.b):x.a<y.a; } void add(int pos,int val) { while(pos<=m) { tarr[pos]+=val; pos+=lowbit(pos); } } int query(int pos) { int res=0; while(pos) { res+=tarr[pos]; pos-=lowbit(pos); }return res; } void CDQ(int l,int r) { if(l==r)return; int mid=(l+r>>1); CDQ(l,mid);CDQ(mid+1,r); int i=l,j=mid+1,k=l; while(i<=mid&&j<=r) { if(e[i].b<=e[j].b)add(e[i].c,e[i].w),t[k++]=e[i++]; else e[j].f+=query(e[j].c),t[k++]=e[j++]; } while(i<=mid)add(e[i].c,e[i].w),t[k++]=e[i++]; while(j<=r)e[j].f+=query(e[j].c),t[k++]=e[j++]; for(int i=l;i<=mid;i++)add(e[i].c,-e[i].w); for(int i=l;i<=r;i++)e[i]=t[i]; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c),e[i].w=1; sort(e+1,e+1+n,cmp); cnt=1; for(int i=2;i<=n;i++) { if(e[i].a==e[cnt].a&&e[i].b==e[cnt].b&&e[i].c==e[cnt].c) e[cnt].w++; else e[++cnt]=e[i]; } CDQ(1,cnt); for(int i=1;i<=cnt;i++)ans[e[i].f+e[i].w-1]+=e[i].w; for(int i=0;i<n;i++)printf("%d\n",ans[i]); }
-
Application:
This is a three-dimensional partial ordering problem. We regard time as the first dimension, x as the second dimension, and y as the third dimension.
According to the idea of dealing with three-dimensional partial order, we first sorted (t > x > y), merged x, and made a tree array of Y.
However, the query on this question is rather annoying: it queries the matrix prefix and the matrix prefix. So for each query, we need to deal with the prefix sum of four intervals. Here I split one inquiry into four.
See code for details:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define lowbit(x) (x&-x)
using namespace std;
const int W=2000010;
const int Q=200010;
struct node
{
int id,x,y,t,sum;//id For time, t Type( add still query),t=0,sum For added values,
//t=0,sum For return value
}e[Q],t[Q];
int cnt;
int tarr[W];
inline 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();}
return x*f;
}
int w;
bool cmp(node a,node b)
{
return a.id==b.id?(a.x==b.x?a.y<b.y:a.x<b.x):a.id<b.id;
}
bool cmp2(node a,node b)
{
return a.id<b.id;
}
void add(int pos,int val)
{
while(pos<=W)
{
tarr[pos]+=val;
pos+=lowbit(pos);
}
}
int query(int pos)
{
int res=0;
while(pos)
{
res+=tarr[pos];
pos-=lowbit(pos);
}return res;
}
void CDQ(int l,int r)//CDQ There is no essential difference between divide-and-conquer and merge boards.
{
if(l==r)return;
int mid=(l+r>>1);
CDQ(l,mid);CDQ(mid+1,r);
int i=l,j=mid+1,k=l;//basic operation
while(i<=mid&&j<=r)
{
if(e[i].x<=e[j].x)//Merge the second dimension, and if the conditions are satisfied, put the contribution of the third dimension into the tree array.
{
if(e[i].t==0)add(e[i].y,e[i].sum);//here t Need is 0
t[k++]=e[i++];
}
else
{
if(e[j].t==1)e[j].sum+=query(e[j].y);//If the conditions have not been met x Now, let's do the statistics.
t[k++]=e[j++];//here t Need to be 1
}
}
while(i<=mid)
{
if(e[i].t==0)
add(e[i].y,e[i].sum);
t[k++]=e[i++];
}
while(j<=r)
{
if(e[j].t==1)
e[j].sum+=query(e[j].y);
t[k++]=e[j++];
}//Statistics the rest
for(int i=l;i<=mid;i++)if(e[i].t==0)add(e[i].y,-e[i].sum);//Tree arrays must be cleared
for(int i=l;i<=r;i++)
e[i]=t[i];
}
int main()
{
while(1)
{
int cid=read();
if(cid==0)w=read();
if(cid==1)
{
int x=read()+1,y=read()+1,z=read();
e[++cnt]=(node){cnt,x,y,0,z}; //Structural Direct Reading
}
if(cid==2)
{
int i=read(),j=read(),x=read()+1,y=read()+1;//x,y Probably 0, the tree array will explode
e[++cnt]=(node){cnt,i,j,1,0};//So they all have to add one and have no effect on the results.
e[++cnt]=(node){cnt,i,y,1,0};
e[++cnt]=(node){cnt,x,j,1,0};
e[++cnt]=(node){cnt,x,y,1,0};//One inquiry is divided into four inquiries
}
if(cid==3)break;
}
sort(e+1,e+1+cnt,cmp);//Sorting the first dimension
CDQ(1,cnt);
sort(e+1,e+1+cnt,cmp2);//Before querying, it must be arranged according to the time, because it has been merged in the second dimension.
for(int i=1;i<=cnt;i++)
{
int ans=0;
if(e[i].t==1)
{
int a=e[i].sum,b=e[i+1].sum,c=e[i+2].sum,d=e[i+3].sum;
ans=a-b-c+d;printf("%d\n",ans);//When you encounter a query, the final answer is to merge the four queries.
i+=3;
}
}
}
Two-dimensional partial order is very easy to understand, and three-dimensional partial order is too difficult to draw, so there is no drawing here.
Perfect scattering of flowers
-Thranduil