区间第k大问题 权值线段树 hdu 5249
先说下权值线段树的概念吧
权值平均树 就是指区间维护值为这个区间内点出现次数和的线段树
用这个加权线段树 解决第k大问题就很方便了
int query(int l,int r,int rt,int k)//找第k大的数 { if(l==r) return l; int m=(l+r)/2; if(k<=sum[rt<<1]) return query(lson,k);//看左儿子的sum是否大于k大于的话 说明第k大的树在左儿子(利用出现的次数进行比对---建树的时候 边界是递增的) else return query(rson,k-sum[rt<<1]); }
最后说一下使用权值线段树容易出现的问题 在建树的过程中 树的最大边界得包括我们需要的数据 那么问题来了 当数据很大的时候 不久爆内存了
这里得将数据离散化一下
什么是离散化呢 就是将数据映射成紧凑的数据 三个步骤
1.排序 2.去重 unqinue()函数。。 3.二分定位 用位置映射数据(lower_bound函数。。)
附上吴迎学长离散化的标准代码
#include<iostream> #include<algorithm> #include<cstdio> using namespace std; int a[100],b[100],c[100],n; int solve()//离散化 { for(int i=0;i<n;i++) b[i]=a[i]; sort(b,b+n); int m=unique(b,b+n)-b;//去重 for(int i=0;i<n;i++) c[i]=lower_bound(b,b+m,a[i])-b;//二分找对应位置 for(int i=0;i<n;i++) printf("%d ",c[i]); } int main() { freopen("in.txt","r",stdin); cin>>n; for(int i=0;i<n;i++) cin>>a[i]; solve(); return 0; }
再说说5249吧。,。 看了下吴迎学长的代码。。 感触挺深。 自己按照思路码了一遍 库函数好用
#include<cstdio> #include<string.h> #include<iostream> #include<algorithm> #include<queue> #define lson l,m,rt<<1 //显得代码比较精简 #define rson m+1,r,rt<<1|1 #define maxn 10005 using namespace std; int sum[maxn<<2]; int n,a[maxn],b[maxn],c[maxn]; void Pushup(int rt) { sum[rt]=sum[rt<<1]+sum[rt<<1|1]; } void build(int l,int r,int rt) { if(l==r) { sum[rt]=0; return ; } int m=(l+r)/2; build(lson); build(rson); Pushup(rt); } void updata(int l,int r,int rt,int temp,int flag) { if(l==r) { sum[rt]+=flag; return; } int m=(l+r)/2; if(temp<=m) updata(lson,temp,flag);// else updata(rson,temp,flag); Pushup(rt); } int query(int l,int r,int rt,int k)//找第k大的数 { if(l==r) return l; int m=(l+r)/2; if(k<=sum[rt<<1]) return query(lson,k); else return query(rson,k-sum[rt<<1]); } int main() { int t=0; while(~scanf("%d",&n))//由于需要离散化的过程 我们需要离线写 { char op[10]; queue<int> que; // freopen("in.txt","r",stdin); for(int i=1;i<=n;i++) { scanf("%s",op); if(op[0]=='i') scanf("%d",&a[i]); else if(op[0]=='o') a[i]=-1; else a[i]=-2; } int m=1; for(int i=1;i<=n;i++) { if(a[i]>=0) b[m]=a[i],c[m++]=a[i]; } sort(c+1,c+m); int mm=unique(c+1,c+m)-(c+1);//离散的排序 去重 unique函数的参数为需要去重的范围 返回值为最后一个去重过程的函数 还有就是这个函数只对相邻的数去重 build(1,mm,1); printf("Case #%d:\n",++t); // cout<<mm<<endl; // for(int i=1;i<=mm;i++) cout<<c[i]<<endl; for(int i=1;i<=n;i++) { if(a[i]>=0) { int temp=lower_bound(c+1,c+mm+1,a[i])-c;//离散坐标对应 updata(1,mm,1,temp,1); // cout<<temp<<endl; // for(int i=1;i<=7;i++) cout<<"in"<<sum[i]<<endl; que.push(a[i]); } else if(a[i]==-1) { int ret=que.front(); int temp=lower_bound(c+1,c+mm+1,ret)-c;// 在c数组中找到第一个不小于ret的数 updata(1,mm,1,temp,-1); //for(int i=1;i<=7;i++) cout<<' '<<"out"<<sum[i]<<endl; que.pop(); } else { // cout<<que.size()/2+1<<endl; printf("%d\n",c[query(1,mm,1,(que.size()/2+1))]); } } } return 0; }