题目大意:给出三个排列A,B,C,问同时满足Ax < Ay,Bx < By,Cx < Cy的数对有多少。
O(n log^2 n)的做法:经典的三维数点问题,排序一维,CDQ分治一维,树状数组一维。
O(n log n)的做法:因为均为排列,所以没有相同的元素,设S(x,y)=[Ax < Ay]+[Bx < By]+[Cx < Cy]。显然max(S(x,y),S(y,x))的取值只有2或3,假设取值为2的有p种,为3的有q种,p+q=n*(n-1)/2。设P(A,B)=只考虑A,B两个排列的答案,3 *p+q=P(A,B)+P(B,C)+P(A,C)。二维数点求出P即可。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
ll seed,ans=0;
int n,t[2000100];
struct node
{
int a,b,c;
}p[2000100];
ll Rand()
{
return (seed = ((seed*19260817) ^ 233333) & ((1<<24)-1));
}
inline bool cmpa(node x,node y){return x.a<y.a;}
inline bool cmpb(node x,node y){return x.b<y.b;}
inline bool cmpc(node x,node y){return x.c<y.c;}
inline int lowbit(int x){return x&(-x);}
void add(int x)
{
while(x<=n)
{
t[x]++;
x+=lowbit(x);
}
}
int query(int x)
{
int re=0;
while(x>0)
{
re+=t[x];
x-=lowbit(x);
}
return re;
}
int main()
{
freopen("dalao.in","r",stdin);
freopen("dalao.out","w",stdout);
scanf("%d",&n);
scanf("%lld",&seed);
for(int i = 1; i <= n; i++) p[i].a = i;
for(int i = 1; i <= n; i++) swap(p[i].a, p[Rand()%i + 1].a);
scanf("%lld",&seed);
for(int i = 1; i <= n; i++) p[i].b = i;
for(int i = 1; i <= n; i++) swap(p[i].b, p[Rand()%i + 1].b);
scanf("%lld",&seed);
for(int i = 1; i <= n; i++) p[i].c = i;
for(int i = 1; i <= n; i++) swap(p[i].c, p[Rand()%i + 1].c);
sort(p+1,p+n+1,cmpa);
memset(t,0,sizeof(t));
for(int i=1;i<=n;i++)
{
add(p[i].b);
ans+=query(p[i].b-1);
}
memset(t,0,sizeof(t));
for(int i=1;i<=n;i++)
{
add(p[i].c);
ans+=query(p[i].c-1);
}
sort(p+1,p+n+1,cmpb);
memset(t,0,sizeof(t));
for(int i=1;i<=n;i++)
{
add(p[i].c);
ans+=query(p[i].c-1);
}
printf("%lld\n",(ans-(ll)n*(n-1)/2)/2);
}
/*4
201334450
1474105774
350932494
*/