JZOJ 4018.【雅礼联考DAY02】Magic

题目大意

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

解题思路

总的来说,三条线组成的,共有5中图形。
这里写图片描述
我们要算的,是2和5的答案,但是直接算不好算。
所以算1,3,4的,减去他们的就好了。
首先算1,要知道一点,一条弦,将一个圆分割成2部分,其中一定有一部分的点是连续的。所以,可以将圆看成线段,然后求不同种类的线段数。
这里写图片描述
如图,红色的线段是要处理的线段,则四种蓝色线段是要求的。
基本思想:用树状数组记录端点的个数。
树状数组2个。
先将所有的左端点加入树状数组1。
设X[i]表示1,2,3的个数,Y[i]表示4的个数,Z[i]表示与红色线段相交的线段条数。
正难则反,算了X[i]和Y[i],可直接得出Z[i]。
其中,在扫的过程中,扫到一个左端点,将其右端点加入树状数组2。
其中种类1和2的,分别在树状数组查询红色线段左端点的蓝色线段的右端点的个数;和红色线段右端点的蓝色线段的左端点的个数。
种类3和种类4的稍微难一点,
拿种类3的来说吧,种类4的计算类似。
直接在碰到红色线段右端点时计算右端点右边蓝色线段的右端点数会将2的算进去。
那么在碰到红色线段左端点时计算2就好了。
然后就可以愉快地计算答案了。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define N 100010
#define LL long long
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
struct note{
    int l,r,id;
};note a[N];
struct note1{
    int d,id;
};note1 b[N*2];
int i,j,k,n,m,lim,sum;
int X[N],Y[N],Z[N];
int tr[2][N*2];
LL ans;
int read(){
    int fh=1,rs=0;char ch;
    while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
    if(ch=='-')fh=-1,ch=getchar();
    while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
    return fh*rs;
}
bool cmp(note1 a,note1 b){return a.d<b.d;}
int lowbit(int x){return x&(-x);}
int query(int x,int y){
    int rs=0;
    for(;x;x-=lowbit(x))rs+=tr[y][x];
    return rs;
}
void add(int x,int y){
    for(;x<=lim;x+=lowbit(x))tr[y][x]++;
}
int main(){
    n=read();
    fo(i,1,n){
        a[i].l=read(),a[i].r=read(),a[i].id;
        if(a[i].l>a[i].r)swap(a[i].l,a[i].r);
    }
    fo(i,1,n){
        b[i*2-1].d=a[i].l;
        b[i*2-1].id=i;
        b[i*2].d=a[i].r;
        b[i*2].id=i+n;
    }
    sort(b+1,b+n*2+1,cmp);
    lim=n*2;
    fo(i,1,n)add(a[i].l,0);
    fo(i,1,lim){
        if(b[i].id>n){
            k=b[i].id-n;
            X[k]+=query(a[k].l,1)+query(lim,0)-query(a[k].r-1,0);
            Y[k]=query(a[k].r-1,1)-query(a[k].l-1,1)-Y[k];
        }else{
            add(a[b[i].id].r,1);
            X[b[i].id]=query(lim,1)-query(a[b[i].id].r,1);
            Y[b[i].id]=query(a[b[i].id].r-1,1)-query(a[b[i].id].l-1,1);
        }
    }
    fo(i,1,n)Z[i]=n-1-X[i]-Y[i];
    fo(i,1,n){
        ans+=1ll*X[i]*Y[i];
        ans+=1ll*(X[i]+Y[i])*Z[i]/2;
    }
    ans=1ll*n*(n-1)*(n-2)/6-ans;
    printf("%lld",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值