4018. 【雅礼联考DAY02】Magic

Description

圆上有 2 ∗ n 个点和连接这些点的 n 条弦,这些弦不会在圆上相交。这2 ∗ n 个点按照在圆上的位置顺序依次标号为 1,2,...,2 ∗ n。
请求出有多少个无序的三元组,使得对应的三条弦可以通过距离的缩放中心对称。

Input

第一行一个数 n (n ≤ 100000)。
接下来 n 行,每行两个数,表示该弦的端点。保证一个数不会出现两次。

Output

输出一个数,表示方案数。

Sample Input

样例输入1:
4
5 4
1 2
6 7
8 3
样例输入2:
8
1 7
2 4
3 9
5 11
6 8
10 16
13 15
14 12

Sample Output

样例输出1:2
样例输出2:6

Data Constraint

对于30%数据,n ≤ 100.
对于60%数据,n ≤ 10000.
对于100%数据,n ≤ 100000.

Hint

1. 什么是距离缩放?
无视圆上的点之间的绝对距离,只考虑其相对顺序。
2. 何谓中心对称?
旋转 2π /3 后与原图形相同。

Solution

题目大意实际上就是给你圆上的n条弦,让你求出三条弦不相交或两两相交的方案数。

我们要算的,是2和5的答案,但是直接算不好算。 
所以算1,3,4的,减去他们的就好了。

我们将圆从一个点(1号点)断开变成一条线段,并且将每条弦看成从较小编号的点到编号大的点的一条弦(不管它所对的弧是优弧还是劣弧)。

那么就变成了如图所示。情况一可以视作由图中(蓝色3、红色、蓝色4)所构成的一种方案,而情况二可以视作(蓝色1、蓝色4、蓝色2)构成的方案。

那么对于每条线段,我们分别求出它的x[i],y[i],z[i]=n-1-x[i]-y[i]。

那么对应的情况一的方案为x[i]*y[i],情况三、四的总和为z[i]*(x[i]+y[i]),最后我们要将情况三四的总和除以2,因为在相交的两条弦的每一条中我们都计算了一次答案,接下来讲讲怎么求x[i]和y[i]。

x[i]=蓝色线段1、2、3的总和(即能够包含当前线段的线段个数)。

y[i]=蓝色线段4的总和(即包含在当前线段中的线段个数)。

分开求,可以将线段左右端点拆开,从小到大加入。

对于线段1,我们可以在处理到当前线段左端点时统计前面的右端点的个数。

对于线段2,处理到右端点时统计后面的左端点的个数

但是如果我们的左端点按顺序加入就统计不到线段2,所以我们先将所以的左端点加入树状数组中,这样不影响。

对于线段3,我们可以在遇到每个左端点时将右端点加入第二个树状数组中,那么它就是当前(在左端点以前)加入树状数组中的右端点在这条线段右端点右边的个数。

对于线段4,可以在到这条线段左端点时统计一次这条线段内部的右端点的个数,到这条线段右端点时再统计一次,两次作差就可以得到内部的线段的个数。

最后答案为Ans=\binom{n}{3}-\sum{x[i]*y[i]+(x[i]+y[i])*z[i]/2.。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define I int
#define ll long long
#define A(op) a[i].op
#define F(i,a,b) for(register int i=a;i<=b;++i)
#define N 100004
using namespace std;
I n,m,k,l,r,t,c[2][N<<1],b[N<<1];
ll ans,x[N],y[N];
struct node{I x,d;}a[N<<1];
I cmp(node x,node y){return x.x<y.x;}
void add(I x,I y){for(;x<=t;x+=x&-x) c[y][x]++;}
ll ask(I x,I y){ll st=0;for(;x;x-=x&-x) st+=c[y][x];return st;}
I main(){
	freopen("Magic.in","r",stdin);
	freopen("Magic.out","w",stdout);
	scanf("%d",&n);
	F(i,1,n){
		scanf("%d%d",&l,&r);if(l>r) swap(l,r);
		a[++t]=node{l,i},a[++t]=node{r,i+n};
	}
	sort(a+1,a+1+n*2,cmp);
	F(i,1,t){
		b[A(d)]=i;
		if(A(d)<=n) add(A(x),0);
	}
	F(i,1,t){
		k=A(d)-(A(d)>n)*n;
		if(A(d)>n){
			x[k]=x[k]-ask(A(x),0);
			y[k]=ask(A(x)-1,1)-ask(a[b[k]].x,1)-y[k];
		}
		else{
			add(a[b[k+n]].x,1);
			x[k]+=ask(A(x),1)+ask(t,0)+ask(n*2,1)-ask(a[b[k+n]].x,1);
			y[k]=ask(a[b[k+n]].x-1,1)-ask(A(x),1);
		}
	}
	ans=(ll)n*(n-1)*(n-2)/6;
	F(i,1,n) ans=ans-x[i]*y[i]-(x[i]+y[i])*(n-1-x[i]-y[i])/2;
	printf("%lld\n",ans);
	return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值