题解:HDU 7439

Link

1. Description

给出长度为 \(n\) 的两个序列 \(f,g\),按照以下方式生成一张图:

for i from 1 to n:
    for j from i+1 to n:
        if f[i] < f[j] and g[i] < g[j]:
            add edge from i to j
        else:
            add edge from j to i

询问图中有多少个三元环。

2. Solution

这样生成出来的显然是一张竞赛图(在一张无向完全图中给每条边定向得到的有向图),我们在这张图中随意取出三个点,三个点中显然都有边相连,我们只需要保证这些边的方向均正确即可。

考虑边的方向不正确的情况,必定有一个点的出度为 \(2\),换句话说,令一个点的出度为 \(d\),那么先选了这个点,然后再选两个点,不构成三元环的情况有 \(C_d^2\) 种,我们只需要在所有情况中减掉不构成三元环的情况即可。

也就说,答案等于 \(C_n^3-\sum_{i=1}^n C_{d_i}^2\),其中 \(d_i\) 表示点 \(i\) 的出度。

现在我们就把问题转换为了求每个点的出度,显然是两个三维偏序问题.

对于 \(i\),分别考虑 \(j\) 大于 \(i\) 和小于 \(i\) 两种情况:对于 \(j\) 大于 \(i\) 的情况,需要 \(f_j>f_i\)\(g_j>g_i\),对于 \(j\) 小于 \(i\) 的情况,可以先将所有 \(j\) 视为合法的,然后去掉不合法的情况,也就是 \(f_j<f_i\)\(g_j<g_i\) 的情况。

一共做两遍 CDQ 分治即可。

3. Code

/*by qwer6*/
/*略去缺省源和快读快写*/
const int N=2e5+5;
int n;
ll ans;
int f[N],g[N],d[N];
struct Query{
	int id,x,y;
	bool operator <(const Query &a)const{
		return id<a.id;
	}
	bool operator >(const Query &a)const{
		return id>a.id;
	}
}a[N],b[N];
struct Binary_tree{
	int c[N];
	#define lowbit(x) (x&-x)
	void add(int x,int v){
		for(int i=x;i<=n;i+=lowbit(i))c[i]+=v;
	}
	int query(int x){
		int res=0;
		for(int i=x;i;i-=lowbit(i))res+=c[i];
		return res;
	}
	int query(int l,int r){
		return query(r)-query(l-1);
	}
	#undef lowbit
}bit;
void CDQ1(int l,int r){
	if(l==r)return ;
	int mid=l+r>>1;
	CDQ1(l,mid),CDQ1(mid+1,r);
	for(int i=l,L=l,R=mid+1;i<=r;i++){
		if(R>r||L<=mid&&a[L].x>a[R].x){
			b[i]=a[L++];
			bit.add(b[i].y,1);
		}else{
			b[i]=a[R++];
			d[b[i].id]+=bit.query(b[i].y+1,n);
		}
	}
	for(int i=l;i<=mid;i++){
		bit.add(a[i].y,-1);
		a[i]=b[i];
	}
	for(int i=mid+1;i<=r;i++)a[i]=b[i];
}
void CDQ2(int l,int r){
	if(l==r)return ;
	int mid=l+r>>1;
	CDQ2(l,mid),CDQ2(mid+1,r);
	for(int i=l,L=l,R=mid+1;i<=r;i++){
		if(R>r||L<=mid&&a[L].x<a[R].x){
			b[i]=a[L++];
			bit.add(b[i].y,1);
		}else{
			b[i]=a[R++];
			d[b[i].id]-=bit.query(1,b[i].y-1);
		}
	}
	for(int i=l;i<=mid;i++){
		bit.add(a[i].y,-1);
		a[i]=b[i];
	}
	for(int i=mid+1;i<=r;i++)a[i]=b[i];
}
signed main(){
	read(n);
	for(int i=1;i<=n;i++)read(f[i]);
	for(int i=1;i<=n;i++)read(g[i]);	
	for(int i=1;i<=n;i++){
		a[i]={i,f[i],g[i]};
		d[i]=i-1;
	}
	sort(a+1,a+n+1,greater<Query>());
	CDQ1(1,n);
	sort(a+1,a+n+1);
	CDQ2(1,n);
	ans=1ll*n*(n-1)*(n-2)/6;
	for(int i=1;i<=n;i++)
		if(d[i]>=2)
			ans-=1ll*d[i]*(d[i]-1)/2;
	write(ans);
}
posted @ 2025-04-17 19:33  陈牧九  阅读(13)  评论(0)    收藏  举报