Jzoj5608 Subset

227 篇文章 3 订阅
153 篇文章 0 订阅

这道题非常有意思

首先我们可以发现,对于一个合法的三元组我们可以找到一个大小小于3的下标集合S与之对应

那么我们就来考虑找到所有合法的这样的S

当|S|=1时,显然都是成立的

当|S|=2时,我们只需要保证s中两个元素i,j不满足a[i]<a[j],b[i]<b[j],c[i]<c[j]就可以了

考虑计算不满足的方案,可以用cdq分治+fenwick完成

当|S|=3时,我们直接计算比较困难,我们考虑两种不合法的情况

1.存在一个元素x满足a[x],b[x],c[x]都是最大的

2.存在一个元素x满足a[x],b[x],c[x]中有两个是最大的

对于第一种情况,我们可以沿用计算|S|=2时的结果,乘上一个组合数,设这部分的答案为A

对于第二种情况,我们可以考虑容斥,先设这部分的答案为B

让后我们可以考虑计算对于一个元素x,a[x],b[x],c[x]存在至少两个是最大的

直接枚举哪两个是最大的让后按照其中一个排序,另一个用fenwick维护

那么设这样计算的答案为X,显然有X=A*3+B 那么我们就得到了B

用总方案数减掉A和B就是|S|=3时的答案

好题!

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,w[100010]; long long A,B,X,S=0;
struct dt{ int a,b,c,G; } s[100010],b[100010];
inline bool c1(dt a,dt b){ return a.a<b.a; } 
inline bool c2(dt a,dt b){ return a.b<b.b; } 
void cdq(int l,int r){
	if(l==r) return;
	int m=l+r>>1,i,j,t=l,a=s[m].a;
	cdq(l,m); cdq(m+1,r);
	for(i=l,j=m+1;i<=m && j<=r;)
		if(s[i].b<s[j].b){
			for(int x=s[i].c;x<=n;x+=x&-x) ++w[x];
			b[t++]=s[i++];
		} else {
			for(int x=s[j].c;x;x&=x-1) s[j].G+=w[x];
			b[t++]=s[j++];
		}
	for(;i<=m;){
		for(int x=s[i].c;x<=n;x+=x&-x) ++w[x];
		b[t++]=s[i++];
	}
	for(;j<=r;){
		for(int x=s[j].c;x;x-=x&-x) s[j].G+=w[x];
		b[t++]=s[j++];
	}
	for(;l<=r;++l){
		s[l]=b[l];
		if(s[l].a<=a) 
		for(int x=s[l].c;x<=n;x+=x&-x) --w[x];
	}
}
int main(){
	freopen("subset.in","r",stdin);
	freopen("subset.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;++i) scanf("%d",&s[i].a);
	for(int i=1;i<=n;++i) scanf("%d",&s[i].b);
	for(int i=1;i<=n;++i) scanf("%d",&s[i].c);
	sort(s+1,s+1+n,c1); cdq(1,n);
	for(int i=1;i<=n;++i) A+=(s[i].G*(s[i].G-1ll)>>1);
	sort(s+1,s+1+n,c1);
	int w[100010]={0};
	for(int i=1,j,t;i<=n;++i){
		for(t=0,j=s[i].b;j;j&=j-1) t+=w[j];
		for(j=s[i].b;j<=n;j+=j&-j) w[j]++;
		X+=t*(t-1ll)>>1;
	}
	memset(w,0,sizeof w);
	for(int i=1,j,t;i<=n;++i){
		for(t=0,j=s[i].c;j;j&=j-1) t+=w[j];
		for(j=s[i].c;j<=n;j+=j&-j) w[j]++;
		X+=t*(t-1ll)>>1;
	}
	sort(s+1,s+1+n,c2);
	memset(w,0,sizeof w);
	for(int i=1,j,t;i<=n;++i){
		for(t=0,j=s[i].c;j;j&=j-1) t+=w[j];
		for(j=s[i].c;j<=n;j+=j&-j) w[j]++;
		X+=t*(t-1ll)>>1;
	}
	B=X-A*3; S=n*(n+1ll)>>1;
	for(int i=1;i<=n;++i) S-=s[i].G;
	S+=n*(n-1ll)*(n-2ll)/6; S-=A+B;
	printf("%lld\n",S);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值