归并排序都不知道怎么做的本蒟蒻今天考试被虐,于是去学了一发三维偏序+cdq分治。
一些相关的博文:
三维偏序cdq:点击打开链接
陌上花开:点击打开链接
归并排序:点击打开链接(以上都没有代码实现过的本蒟蒻)
题意:
n 支队伍一共参加了三场比赛。
一支队伍 x 认为自己比另一支队伍 y 强当且仅当 x 在至少一场比赛中比 y 的排名高。
求有多少组(x,y),使得 x 自己觉得比 y 强,y 自己也觉得比 x 强。
(x, y), (y, x)算一组。
【数据规模】
对于 30%的数据,n≤1000;
对于 100%的数据,n≤200000, 1≤a[i],b[i],c[i]≤n, 且所有的 a[i],b[i],c[i]都是不同的;
题解:
真的是裸的三位偏序cdq啊。分治+容斥+树状数组
第一维sort排序,第二维树状数组,第三维分治cdq
分的时候标记一下就可以求出左区间和右区间不满足的数量啦
ans为满足三位偏序的序列,cdq的时候保证l中的z
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll n,ans,tre[N];
struct w{
ll x,y,z;
bool operator <(const w& b)const{
if(x<b.x || x==b.x && y<b.y || x==b.x && y==b.y && z<b.z)
return 1;
return 0;
}
}t[N],L[N],R[N];
inline void update(int x,int adds)
{
for(;x<=n;x+=(x&(-x))) tre[x]+=adds;
}
inline ll query(int x){
ll ans=0;
for(;x>0;x-=(x&(-x))) ans+=tre[x];
return ans;
}
inline void cdq(int l,int r){
ll mid=(l+r)>>1;
if(l==r) return;
for(ll i=l;i<=r;i++){
if(t[i].z<=mid)
update(t[i].y,1);
else
ans+=query(t[i].y);//1-t[i].y的数量-即在右区间不满足
}
for(ll i=l;i<=r;i++){
if(t[i].z<=mid) update(t[i].y,-1);//recover
}
ll p=0,q=0;
for(ll i=l;i<=r;i++){
if(t[i].z<=mid) L[++p]=t[i];
else R[++q]=t[i];//第三维分治
}
for(ll i=l;i<=mid;i++) t[i]=L[i-(l-1)];
for(ll i=mid+1;i<=r;i++) t[i]=R[i-mid];
cdq(l,mid);cdq(mid+1,r);
}
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&t[i].x,&t[i].y,&t[i].z);
}
sort(t+1,t+n+1);
cdq(1,n);
printf("%lld\n",n*(n-1)/2-ans);
return 0;
}
太弱了orzzz