【三维偏序】比赛-cdq分治

归并排序都不知道怎么做的本蒟蒻今天考试被虐,于是去学了一发三维偏序+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

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值