题目大意:
以从左往右,从后往前的顺序给出一些线段。最后从前面可以看见多少条线段?
思路:
模拟?100%超时
离散?100%爆内存
所以,这道题的最优解是—— 我也不知道
———下面进入正题———
正解:线段树
一道模板题吧。
对于tree[x]:
tree[x].l为它的左端点
tree[x].r为它的右端点
tree[x].cover表示它是否有线段
tree[x*2]为它的左儿子
tree[x*2+1]为它的右儿子
1.建树
2.输入,记录
3.计算总长度,输出,AC
代码:
#include
#include
using namespace std;
int n,sum,m,x,y;
struct N
{
int l,r,cover;
}tree[100001];
void make(int x) //建树
{
if (tree[x].r-tree[x].l<=1) return; //已经是一个点
tree[x*2].l=tree[x].l;
tree[x*2].r=(tree[x].l+tree[x].r)/2;
tree[x*2+1].l=(tree[x].l+tree[x].r)/2;
tree[x*2+1].r=tree[x].r; //儿子的左右断点
make(x*2);
make(x*2+1); //继续建
}
void insert(int x,int l,int r) //插入
{
if (tree[x].cover!=0) return; //已经有线段
if (tree[x].l==l&&tree[x].r==r) //找到线段
{
tree[x].cover=1;
return;
}
int mid=(tree[x].l+tree[x].r)/2;
if (mid>=r) //完全在左边
{
insert(x*2,l,r);
return;
}
if (mid<=l) //完全在右边
{
insert(x*2+1,l,r);
return;
}
insert(x*2,l,mid);
insert(x*2+1,mid,r); //左右都有
}
int count(int x) //计算
{
if (tree[x].cover==1) return tree[x].r-tree[x].l; //该区域有线段
if (tree[x].r<=tree[x].l+1) return 0; //为一个点,退出
return count(x*2)+count(x*2+1); //搜索儿子
}
int main()
{
scanf("%d%d",&m,&n);
tree[1].l=1;
tree[1].r=m; //初始化
make(1); //建树
for (int i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
insert(1,x,y); //插入
}
printf("%d\n",count(1)); //输出
return 0;
}