个人觉得比较坑的线段树的题目,因为题意我就理解了半天
题意:
有N根竖直的杆子,每个杆子都用一个长度,两根杆子可以想见意味着两个杆子在水平
方向上存在一条直线,使得该直线连接两个杆子,并且该水平直线和其他的杆子都没有
交点,问一共有多少对三根杆子的组合使得杆子两两可见。这里要注意的地方有一点是如果只是两个杆子的边界相同,只要他们中间没有间隔他们也能相互看见,比如说y坐标为1——3和3——4是可以相互看见的,但是如果他们中间有一个2——3的杆子他们还是看不见,但是如果3——4的杆子在1——3和2——3之间那么还是没法阻挡,所以题意好坑。个人觉得不科学,也不符合实际。
解题思路就是把所有的线按x轴排序,然后依次向y轴映射,然后覆盖,
不过遇坑爹的边界设定,线段树的设计就要略有改进,也就是把所有的y值乘以2,这样的话就算1——3和2——3之间有3——4就会变为2——6,4——6,6——8那么实际上3——4它也只会覆盖6——8而不会将5覆盖保证了1——3和2——3之间的正常判定。
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <cstdio>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
typedef long long LL;
using namespace std;
const int mx = 16005;
int a[mx * 3];
bool ma[mx >> 1][mx >> 1];
int n,qr,ql,date;
struct K
{
int l,r,x;
} S[mx];
bool cmp(K a,K b)
{
return a.x < b.x;
}
void update(int o = 1,int L = 0,int R = mx)
{
if(ql <= L && qr >= R)
{
a[o] = date;
// printf("o = %d L =%d R = %d date =%d\n",o,L,R,date);
return;
}
int mid = (R + L) >> 1;
if(a[o] != 0)
{
a[o << 1] = a[o << 1 | 1] = a[o];
a[o] = 0;
}
// printf("o = %d L =%d R = %d ql = %d qr =%d date = %d mid = %d\n",o,L,R,ql,qr,date,mid);
if(ql <= mid) update(o << 1,L,mid);
if(qr > mid) update(o << 1|1,mid + 1,R);
}
void qu(int o = 1,int L = 0,int R = mx)
{
// printf("o = %d L =%d R = %d\n",o,L,R);
if(a[o] != 0)
{
// printf("a[] = %d date =%d\n",a[o],date);
ma[a[o]][date] = ma[date][a[o]] = true;
return;
}
if(R == L) return;
// printf("PPPPPP\n");
int mid = (R + L) >> 1;
if(ql <= mid) qu(o << 1,L,mid);
if(qr > mid) qu(o << 1|1,mid + 1,R);
}
int main ()
{
int T,i,h,k,g,j,ans;
scanf("%d",&T);
while (T--)
{
memset(a,0,sizeof(a));
memset(ma,false,sizeof(ma));
ans = 0;
scanf("%d",&n);
for(i = 1; i <= n; i++)
scanf("%d%d%d",&S[i].l,&S[i].r,&S[i].x);
sort(S + 1,S + n + 1,cmp);
for(i = 1; i <= n; i++)
{
qr = S[i].r*2;
ql = S[i].l*2;
date = i;
qu();
update();
}
for(i = 1; i <= n; i++)
for(j = i + 1; j <= n; j++)
if(ma[i][j])
for(k = j; k <= n; k++)
if(ma[i][k] && ma[j][k])
ans++;
printf("%d\n",ans);
}
}