[USACO15JAN]踩踏Stampede
题目描述
DJ站在原点上向y轴正半轴看,然后有一群奶牛从他眼前飞过。这些奶牛初始都在第二象限,尾巴在(Xi,Yi),头在(Xi+1,Yi),每Ci秒向右走一个单位。 DJ能看见一匹奶牛当且仅当它身体任意某部位x坐标为0时,没有其它y坐标小于此奶牛的奶牛身体某部位x坐标为0。 问DJ能看见多少奶牛?
输入格式:
第一行:一个数,给出n,表示n头牛。
第二行:三个数,给出Xi和Yi,描述第i头牛的位置,给出Ci,描述这头牛多少秒走一个单位。
输出格式:
一行,给出ans,即DJ能看到多少头牛。
输入输出样例
3 -2 1 3 -3 2 3 -5 100 1
2
样例说明
DJ能看到第一和第二头牛,看不到第三头牛。
思路
首先,每一头牛的Xi,Yi,Ci都可能非常的大,所以用普通的暴力搜索是过不去的,所以就要换一个方法去想问题,题目中给出了速度,路程,那么我们可以很容易就想到时间。以样例为例:第一头牛的头到达y轴的时间记为B1=(-2+1)*(-1)*3=3,第一头牛的尾巴到达y轴的时间记为E1=B1+3=6,就像这样我们能知道在第3秒到第6秒时第一头牛是在y轴上的;同理,我们可以知道第二头牛在第6秒到第9秒时是在y轴上的,第三头牛在第4秒到第5秒时是在y轴上的。有人会问知道这些有什么用呢?由于知道时间和纵坐标的位置,我们可以轻易的推出来任意两头牛是否有重叠,方法是当且仅当第i头牛的时间段与第j头牛的时间段有重叠,并且第i头牛的纵坐标比第j头牛的纵坐标小才能当上第j头牛。
由于我只需要看到这头牛的一部分就算看到,那么我们每一次只需要看一下当前牛的时间段是否被覆盖了,如果没有被全被覆盖则ans++,这头牛可以被看到,并且把这个时间段全部覆盖。那么现在有一个问题了,怎么能保证每一头牛能在恰当的时间进行验证呢?我们需要把牛按纵坐标进行排序,在一一进行验证,这样就可以保证纵坐标小的牛不被纵坐标大的牛遮挡住。既然思路已经出来,那么算法十分容易分析出来,区间查询+区间修改=线段树。我们再看一下每一个数是十分大的,如果直接进行线段树覆盖会MLE,那么是不是可以把时间离散化一下?我们把时间点排序,在更形成序号就可以啦,再看样例这些牛中出现了3,6,9,4,5五个时间点,排序过后是3,4,5,6,9,那么第一头牛就可以化为在第1秒到第4秒在y轴上,第二头牛就可以化为在第4秒到第5秒在y轴上,第一头牛就可以化为在第2秒到第3秒在y轴上。这样每一次操作就十分方便了。
注意:由于时间点重合可能是像2~3,3~4和4~5这样的时间段,那么如果直接按照时间点进行查询和覆盖就可能出现问题,比如说先覆盖2~3和4~5,那么查找3~4时便会出现问题,所以每一次覆盖和查询的是线段,比如2~6是覆盖和查询2~5的线段。
1 #include<stdio.h> 2 #include<algorithm> 3 using namespace std; 4 #define mid ((l+r)>>1) 5 struct Cow{ 6 int high,order_l,order_r; 7 }cow[50001]; 8 struct Num{ 9 int order,order2; 10 int x; 11 }num[100001]; 12 bool cmp1(const Num &a,const Num &b){ 13 return a.x<b.x; 14 } 15 bool cmp2(const Num &a,const Num &b){ 16 return a.order<b.order; 17 } 18 bool cmp3(const Cow &a,const Cow &b){ 19 if(a.high==b.high)return a.order_r-a.order_l<b.order_r-b.order_l; 20 return a.high<b.high; 21 } 22 int n; 23 int idx,idx2; 24 int ans; 25 bool tree[1000000]; 26 void push(int p){ 27 tree[p<<1]=tree[(p<<1)|1]=tree[p]; 28 } 29 bool find(int p,int l,int r,int x,int y){ 30 if(tree[p])push(p); 31 if(l>=x&&r<=y) 32 return tree[p]; 33 bool is1=false,is2=false,is3=false,is4=false; 34 if(x<=mid)is1=find(p<<1,l,mid,x,y),is3=true; 35 if(y>mid)is2=find((p<<1)|1,mid+1,r,x,y),is4=true; 36 if(is1==is3&&is2==is4)return true; 37 return false; 38 } 39 void add(int p,int l,int r,int x,int y){ 40 if(l>=x&&r<=y){ 41 tree[p]=true; 42 return; 43 } 44 if(x<=mid)add(p<<1,l,mid,x,y); 45 if(y>mid)add((p<<1)|1,mid+1,r,x,y); 46 if(tree[p<<1]&&tree[(p<<1)|1])tree[p]=true; 47 } 48 int main(){ 49 scanf("%d",&n); 50 for(int i=1;i<=n;i++){ 51 int a,speed; 52 scanf("%d%d%d",&a,&cow[i].high,&speed); 53 ++idx; 54 num[idx].x=(-1-a)*speed; 55 num[idx].order=idx+1; 56 ++idx; 57 num[idx].x=num[idx-1].x+speed; 58 num[idx].order=idx+1; 59 } 60 sort(num+1,num+idx+1,cmp1); 61 for(int i=1;i<=idx;){ 62 int tmp=0; 63 ++idx2; 64 while(num[i].x==num[i+tmp].x) 65 num[i+tmp].order2=idx2,++tmp; 66 i+=tmp; 67 } 68 sort(num+1,num+idx+1,cmp2); 69 for(int i=1;i<=idx;i++){ 70 (num[i].order%2)? 71 cow[num[i].order/2].order_r=num[i].order2: 72 cow[num[i].order/2].order_l=num[i].order2; 73 } 74 sort(cow+1,cow+n+1,cmp3); 75 for(int i=1;i<=n;i++){ 76 if((!find(1,1,idx2,cow[i].order_l,cow[i].order_r-1))){ 77 ans++; 78 } 79 add(1,1,idx2,cow[i].order_l,cow[i].order_r-1); 80 } 81 printf("%d",ans); 82 }