题目传送门:http://www.cogs.pro/cogs/problem/problem.php?pid=2580
题目分析:又找了一道CDQ分治的裸题,不到30min就切掉了,感觉只要用CDQ分治n维偏序都不成问题……
这题中序列的每个元素有5个属性:编号,a,b,c,d;我们要求有多少对(i,j)使得i的五个属性都小于j。我们不妨先按编号排序,然后进行第一层CDQ,考虑i在左边,j在右边时对答案的贡献,这里要求i的另外四个属性小于j。如果这是三维偏序的话,我们将左半部分的元素变成插入操作,将右半部分的元素变成查询操作,然后将它们堆在一起按第二维排序,以第二维从小到大为时间戳插入以第三维为键值的树状数组中即可。
现在五维偏序也类似,我们将左边的元素变为插入操作,右边的元素变成查询操作(保证所有插入操作的第一维都小于所有查询操作的第一维),然后将它们堆在一起按第二维排序,再进行第二层CDQ。我们又将操作序列划分成两半,计算左半部分的插入对右半部分的查询的影响(保证插入操作的第二维小于查询的第二维),于是又将它们单独拎出来按第三维排一次序,得到一个新序列,用同样的方法进行第三层CDQ。按第四维排完序之后开一个以第五维为键值的树状数组进行插入和查询即可,时间复杂度
O(nlog4(n))
。
CODE:
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;
const int maxn=50010;
struct data
{
int id,a,b,c,d;
bool p;
} num[4][maxn];
int cnt[4];
int bit[maxn];
int n,ans=0;
bool Comp1(data x,data y)
{
return x.a<y.a;
}
bool Comp2(data x,data y)
{
return x.b<y.b;
}
bool Comp3(data x,data y)
{
return x.c<y.c;
}
void Add(int x,int v)
{
while (x<=n)
{
bit[x]+=v;
x+=(x&(-x));
}
}
int Sum(int x)
{
int sum=0;
while (x)
{
sum+=bit[x];
x-=(x&(-x));
}
return sum;
}
void Work()
{
for (int i=1; i<=cnt[3]; i++)
if (!num[3][i].p) Add(num[3][i].d,1);
else ans+=Sum(num[3][i].d);
for (int i=1; i<=cnt[3]; i++)
if (!num[3][i].p) Add(num[3][i].d,-1);
}
void CDQ3(int L,int R)
{
if (L==R) return;
int mid=(L+R)>>1;
cnt[3]=0;
for (int i=L; i<=mid; i++) if (!num[2][i].p) num[3][ ++cnt[3] ]=num[2][i];
for (int i=mid+1; i<=R; i++) if (num[2][i].p) num[3][ ++cnt[3] ]=num[2][i];
if (cnt[3])
{
sort(num[3]+1,num[3]+cnt[3]+1,Comp3);
Work();
}
CDQ3(L,mid);
CDQ3(mid+1,R);
}
void CDQ2(int L,int R)
{
if (L==R) return;
int mid=(L+R)>>1;
cnt[2]=0;
for (int i=L; i<=mid; i++) if (!num[1][i].p) num[2][ ++cnt[2] ]=num[1][i];
for (int i=mid+1; i<=R; i++) if (num[1][i].p) num[2][ ++cnt[2] ]=num[1][i];
if (cnt[2])
{
sort(num[2]+1,num[2]+cnt[2]+1,Comp2);
CDQ3(1,cnt[2]);
}
CDQ2(L,mid);
CDQ2(mid+1,R);
}
void CDQ1(int L,int R)
{
if (L==R) return;
int mid=(L+R)>>1;
cnt[1]=0;
for (int i=L; i<=mid; i++) num[1][ ++cnt[1] ]=num[0][i],num[1][ cnt[1] ].p=false;
for (int i=mid+1; i<=R; i++) num[1][ ++cnt[1] ]=num[0][i],num[1][ cnt[1] ].p=true;
sort(num[1]+1,num[1]+cnt[1]+1,Comp1);
CDQ2(1,cnt[1]);
CDQ1(L,mid);
CDQ1(mid+1,R);
}
int main()
{
freopen("partial_order_two.in","r",stdin);
freopen("partial_order_two.out","w",stdout);
scanf("%d",&n);
for (int i=1; i<=n; i++) num[0][i].id=i;
for (int i=1; i<=n; i++) scanf("%d",&num[0][i].a);
for (int i=1; i<=n; i++) scanf("%d",&num[0][i].b);
for (int i=1; i<=n; i++) scanf("%d",&num[0][i].c);
for (int i=1; i<=n; i++) scanf("%d",&num[0][i].d);
CDQ1(1,n);
printf("%d\n",ans);
return 0;
}