題意:給定n條豎直的線段,求有多少個三元組,使得這三條線段兩兩可見(可見的定義爲這兩條線段可以連一條水平線段而不與任意其他線段相交)
題解:將線段按照x座標排序,判斷兩個線段是否可見,就是將後一個線段向左投影能夠在第一個線段上留下陰影。用線段樹代表某個區間當前陰影代表誰,處理新線段即給那段區間賦值,並記錄會覆蓋哪些值就行。因爲要考慮陰影投射在非整數點,且線段y座標都是整數,所以線段樹的大小要乘二,用2y代表原來的y座標。知道了兩兩可見的情形,枚舉一下就可以出答案了。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=8005;
struct Line
{
int left,right,val;
}line[N*8];
int head[N],nc;
struct Edge
{
int to,next;
}edge[N*1000];
bool g[N][N];
int lis[N];
struct Data
{
int x,y1,y2;
bool operator<(const Data &ne)const
{
return x<ne.x;
}
}data[N];
void add(int a,int b)
{
if(g[a][b])
return;
edge[nc].to=b;edge[nc].next=head[a];head[a]=nc++;
g[a][b]=g[b][a]=true;
//printf("->%d %d\n",a,b);
}
void build(int now,int left,int right)
{
line[now].left=left;
line[now].right=right;
line[now].val=0;
if(left==right)
return;
build(now*2,left,(left+right)>>1);
build(now*2+1,(left+right)/2+1,right);
}
void updata(int now,int ll,int rr,int val)
{
int left=line[now].left,right=line[now].right,mid=(left+right)/2;
if(left==ll&&rr==right&&line[now].val!=-1)
{
if(line[now].val!=0)
add(line[now].val,val);
line[now].val=val;
}
else
{
if(line[now].val!=-1)
{
line[now*2].val=line[now*2+1].val=line[now].val;
line[now].val=-1;
}
if(mid>=rr)
updata(now*2,ll,rr,val);
else if(mid<ll)
updata(now*2+1,ll,rr,val);
else
{
updata(now*2,ll,mid,val);
updata(now*2+1,mid+1,rr,val);
}
}
}
int main()
{
int T;
for(scanf("%d",&T);T;T--)
{
int n,ans=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&data[i].y1,&data[i].y2,&data[i].x);
sort(data+1,data+1+n);
build(1,0,16000);
memset(g,false,sizeof(g));
memset(head,-1,sizeof(head));
nc=0;
for(int i=1;i<=n;i++)
updata(1,data[i].y1*2,data[i].y2*2,i);
for(int i=1,top;i<=n;i++)
{
top=0;
for(int j=head[i];j!=-1;j=edge[j].next)
lis[top++]=edge[j].to;
for(int j=0;j<top;j++)
for(int k=j+1;k<top;k++)
if(g[lis[j]][lis[k]])ans++;
}
printf("%d\n",ans);
}
return 0;
}