0)学会将题目情景转化为自己熟悉的结构或模型。
题目大意:
每个奶牛有自己的一个区间,求每个奶牛的区间所覆盖的子区间个数(注意,真子集,相等的不算),按照输入的顺序输出。
转化:
要学会将题目情景转化为自己熟悉的模型或结构上。把每个区间的左端x值作为点的x坐标,右端x值作为点的y坐标,就可以把所有区间转化为一个二维坐标图上的点集,而此时每个点左上方的点(同Stars那道题目一样不包括自身)的个数,就是每个区间所覆盖的子区间的个数(对应题目要求,这里或许可以再变形)。
同POJ2481 Stars:
不同的是,先线段化点,并对输入的数据进行预处理,即先按y从大到小,y相等的则按x从小到大,以保证,后用来更新树状数组的点不会在先被使用的点的左上方,每输入一个点,则输出该点左上方的点的个数,然后更新树状数组,再输入下一个点。
还记得01背包问题有二维数组转化为一维数组吗,有点相同的思想,这里只用x来做数组的下标,而y则从大到小输入,一边记录各个点的左上方点的个数,一边继续更新树状数组,而树状数组不再对之前的点有可查询性,只保证当前点的可查询性,比如出现这样的点集:(0,3)(1,2)(2,3),更新顺序是,先返回(0,3)左上方的点的个数,然后更新x>=0的所有点,返回(2,3)左上方的点的个数,然后更新x>=2所有的点,返回(1,2)左上方的点的个数,然后更新x>=1的所有的点,这时,x=2的点也会被+1,但因为(2,3)左上方的点已经被记忆到另一个数组或者输出,所以树状数组也不需要再对这个点的左上方有多少点保持记忆,而只是记录当前更新点(1,2)以及其他所有x>=1,y<=2的后面即将输入的点的左上方的当前点的个数。(所以让点按照y从大到小的顺序输入,以保证后用来更新树状数组的点不会在先被使用的点的左上方,所以用哪个点更新就可以确保此时返回的所用的当前点的左上方的点个数是正确的,是不会再增加的。)
1)
#include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int maxn=100010; int n; struct Qujian{ int l;//该区间左端,每个区间化成一个点以后,左端值就是该点x值 int r;//该区间右端,化点后,右端值就是该点y值 int id;//输入次序 //int v; }qujian[maxn]; int stars[maxn];//相当于树状数组的c数组(树状数组的a数组a[x]中,x就是点的x坐标,每个点出现的次数就是a[x]) int value[maxn];//区间化为点后,对应id(输入次序)的点的左上方点的个数 bool cmp1(const Qujian a,const Qujian b){ if(a.r>b.r) return 1; else if(a.r==b.r&&a.l<b.l){ return 1; } else return 0; } bool cmp2(const Qujian a,const Qujian b){ return a.id<b.id; } int Sum(int x){ int res=0; while(x>0){ res+=stars[x]; x-=(x&(-x)); } return res; } void Add(int x,int cou){ while(x<=maxn){ stars[x]+=cou; x+=(x&(-x)); } return ; } int main() { while(~scanf("%d",&n)&&n!=0){ memset(qujian,0,sizeof(qujian)); memset(stars,0,sizeof(stars)); memset(value,0,sizeof(value)); int l; int r; for(int i=0;i<n;i++){ scanf("%d%d",&l,&r); qujian[i].id=i; qujian[i].l=l+1; qujian[i].r=r+1;//保证区间的位置从1开始而不会是0 } sort(qujian,qujian+n,cmp1);//y从大到小,如果y相等则x从小到大,保证后出现的点不会在先出现的点的左上方,所以可以按这个顺序一边更新树状数组,一边求得各个点左上方有多少个点并记录下来 int cou=1;//记录每个点出现的次数 for(int i=0;i<n;i++){ value[qujian[i].id]=Sum(qujian[i].l);//返回坐标为l的点的左上方点的个数(不包括自己),所以先返回再加自己的点进去 if(i+1<n){ if(qujian[i+1].l==qujian[i].l&&qujian[i+1].r==qujian[i].r){//注意,x,y,两个方向上的坐标都要保证相等,如果只看x坐标,那么就把y不同x相同的点误认为是同一个点了。 cou++;//碰到重复区间/相同点,则记录出现次数,等到最后一个进入并且返回其左上方个数之后,再一次性把这些点全部加入 } else{ Add(qujian[i].l,cou); cou=1; } } else if(i+1==n){ Add(qujian[i].l,cou); } } //sort(qujian,qujian+n,cmp2); for(int i=0;i<n-1;i++){ cout<<value[i]<<" "; } cout<<value[n-1]<<endl; } return 0; }
Description
Input
Output
Sample Input
3 1 2 0 3 3 4 0
Sample Output
1 0 0
Hint