题意:
给出一些星星的横坐标和纵坐标,而且星星的纵坐标按非递减排列,如果纵坐标相等,则横坐标按递增排列,任意两颗星星不会重合。如果有n颗星星的横坐标比某颗星星小而且纵坐标不大于那颗星星(即有n颗星星位于那颗星星的左下角或者左边)则此星星的等级为n,最后输出等级为0至n-1的星星的数量。
题解:
一开始用线段树一次就ac了,不过树状数组还是要学一下的,网上说能用树状数组解的一定能用树状数组解,但是如果树状数组和线段树都能解的树状数组一般快一些空间复杂度小一些。。为了理解树状树状我理解了很久。。感觉半懂吧,树状数组只能用于单点更新,感觉是简化版的线段树,然后通过x&(-x)这个神奇的东西将父亲和儿子连接起来,感觉就类似于线段树的num和num*2,num*2+1之间的关系,最后两种方法都写了发现线段树的还快一些不知道为什么
线段树代码:
#include<algorithm> #include<iostream> #include<cstring> #include<stdio.h> #include<math.h> #include<string> #include<stdio.h> #include<queue> #include<stack> #include<map> using namespace std; struct node { int l,r; int sum; }t[32005*4]; int a[15005]; void Build(int l,int r,int num)//日常建树 { t[num].l=l; t[num].r=r; if(l==r) { t[num].sum=0; return; } int mid=(l+r)/2; Build(l,mid,num*2); Build(mid+1,r,num*2+1); t[num].sum=t[num*2].sum+t[num*2+1].sum; } void update(int x,int num)//日常区间更新 { if(t[num].l==x&&t[num].r==x) { t[num].sum++; return; } int mid=(t[num].l+t[num].r)/2; if(x<=mid) update(x,num*2); else update(x,num*2+1); t[num].sum=t[num*2].sum+t[num*2+1].sum; } int query(int l,int r,int num)//日常询问 { if(t[num].l==l&&t[num].r==r) { return t[num].sum; } int mid=(t[num].l+t[num].r)/2; if(r<=mid) return query(l,r,num*2); else if(l>mid) return query(l,r,num*2+1); else return query(l,mid,num*2)+query(mid+1,r,num*2+1); } int main() { int n,i,j,x,y,maxx=0; memset(a,0,sizeof(a)); scanf("%d",&n); Build(0,32000,1); for(i=0;i<n;i++) { scanf("%d%d",&x,&y); a[query(0,x,1)]++; update(x,1); } for(i=0;i<n;i++) { printf("%d\n",a[i]); } return 0;//日常ac }然后是树状数组:
#include<algorithm> #include<iostream> #include<cstring> #include<stdio.h> #include<math.h> #include<string> #include<stdio.h> #include<queue> #include<stack> #include<map> using namespace std; int level[15005]; int sum[32005]; int lowbit(int x)//树状数组与父子关系有关的东西 { return x&(-x); } int Getsum(int x)//树状数组的区间求和,求1到x的和 { int s=0; while(x>0)//一直减到边界,类似于寻找子区间相加 { s+=sum[x]; x-=lowbit(x); } return s; } void update(int x)//树状数组更新 { while(x<32002)//一直加到边界,类似于向上状态传递,加了1所以是32002 { sum[x]++; x+=lowbit(x); } } int main() { int i,j,n,x,y; while(scanf("%d",&n)!=EOF) { memset(sum,0,sizeof(sum)); memset(level,0,sizeof(level)); for(i=0;i<n;i++) { scanf("%d%d",&x,&y); level[Getsum(x+1)]++;//加一是为了防止在x=0时操作时无限循环 update(x+1); } for(i=0;i<n;i++) printf("%d\n",level[i]); } return 0; } ps:
树状数组和的区间c[i],其i转化为二进制最后面有连续的几个0代表子区间有几个,通过-lowbit()可以访问他的子区间和+lowbit()访问他的父亲区间
学习博客:http://blog.csdn.net/qq_34374664/article/details/52787481
