Description

There is a number of disjoint vertical line segments in the plane. We say that two segments are horizontally visible if they can be connected by a horizontal line segment that does not have any common points with other vertical segments. Three different vertical segments are said to form a triangle of segments if each two of them are horizontally visible. How many triangles can be found in a given set of vertical segments? 


Task 

Write a program which for each data set: 

reads the description of a set of vertical segments, 

computes the number of triangles in this set, 

writes the result. 

Input

The first line of the input contains exactly one positive integer d equal to the number of data sets, 1 <= d <= 20. The data sets follow. 

The first line of each data set contains exactly one integer n, 1 <= n <= 8 000, equal to the number of vertical line segments. 

Each of the following n lines consists of exactly 3 nonnegative integers separated by single spaces: 

yi', yi'', xi - y-coordinate of the beginning of a segment, y-coordinate of its end and its x-coordinate, respectively. The coordinates satisfy 0 <= yi' < yi'' <= 8 000, 0 <= xi <= 8 000. The segments are disjoint.

Output

The output should consist of exactly d lines, one line for each data set. Line i should contain exactly one integer equal to the number of triangles in the i-th data set.

Sample Input

1
5
0 4 4
0 3 1
3 4 2
0 2 2
0 2 3

Sample Output

1
【题意】给了你n条线段,问在这n条线段中,最多能组成多少个相互可见的三角形,所谓相互可见,就是用一条直线连接这条线段和那条线段的顶点(这里的线段是垂直于x轴的),那么这道题怎么做呢?
【解题思路】刚看懂这个题的题意,马上想到了一个相似的问题,贴海报,那么解决这道题的最优策略是怎样的呢?当然是对输入的一堆线段按照高度排序之后,做成线段树就好了,线段树需要增加一个标记记录覆盖当前区间的线段编号,这里为col。做成线段树之后,由于这里有区间更新,要考虑Push_Down,接下来就是手动推一下样例,但是怎么推怎么不对(-><-),看了一下别人的blog,发现了一个新问题,例如0,4,1 和 0,2,2 和 3,4,2。这个数据就能说明问题了,如果不乘以2 那么 第二条 和 第三条就把 x=2 这一段堵死了,其实还有2-3这一个线段的。解决了这个问题,本题就完美解决了。实现细节见我的AC代码。
【AC代码】
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
const int nn = 8005;
bool vis[nn][nn];
struct seg{
    int l,r,h;
    friend bool operator<(const seg &a,const seg &b)
    {
        return a.h<b.h;
    }
}seg[nn];

struct node{
   int l,r,col;
}Tree[nn<<3];
void Push_Down(int rt)
{
    if(Tree[rt].col!=-1)
    {
        Tree[rt<<1].col=Tree[rt<<1|1].col=Tree[rt].col;
        Tree[rt].col=-1;
    }
}

void Build(int l,int r,int rt)
{
    Tree[rt].l=l,Tree[rt].r=r,Tree[rt].col=-1;
    if(l==r)return ;
    int m = (l+r)>>1;
    Build(lson);
    Build(rson);
}

void Update(int L,int R,int val,int rt)
{
    if(L<=Tree[rt].l&&Tree[rt].r<=R)
    {
        Tree[rt].col = val;
        return ;
    }
    Push_Down(rt);
    int m = (Tree[rt].l+Tree[rt].r)>>1;
    if(L<=m) Update(L,R,val,rt<<1);
    if(m<R)  Update(L,R,val,rt<<1|1);
}
void query_ans(int L,int R,int val,int rt)//询问和当前线段可以相互可见的线段
{
    if(Tree[rt].col!=-1)
    {
        vis[Tree[rt].col][val] = true;
        return ;
    }
    if(Tree[rt].l==Tree[rt].r)return ;
    int m =(Tree[rt].l+Tree[rt].r)>>1;
    if(L<=m) query_ans(L,R,val,rt<<1);
    if(m<R) query_ans(L,R,val,rt<<1|1);
}


int main()
{
    ios::sync_with_stdio();cin.tie(0);
    int T,n;
    scanf("%d",&T);
    while(T--)
    {
        cin>>n;
        for(int i=1; i<=n; i++)
        {
            scanf("%d%d%d",&seg[i].l,&seg[i].r,&seg[i].h);
            seg[i].l<<=1,seg[i].r<<=1;
        }
        sort(seg+1,seg+n+1);
        Build(1,nn*2,1);
        memset(vis,0,sizeof(vis));
        for(int i=1; i<=n; i++)
        {
            query_ans(seg[i].l,seg[i].r,i,1);
            Update(seg[i].l,seg[i].r,i,1);
        }
        int ans = 0;
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=n; j++)
            {
                if(vis[i][j])
                {
                    for(int k=1; k<=n; k++)
                    {
                        if(vis[i][k]&&vis[j][k]) ans++;
                    }
                }
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}