poj_2985 The k-th Largest Group 树状数组求第K大
来源:互联网 发布:卡尔曼滤波原理和算法 编辑:程序博客网 时间:2024/05/22 00:07
题目链接:http://poj.org/problem?id=2985
题目大意:某人养了很多猫,他会把一些猫合并成一组,并且会询问第k大组有几只猫
前面说了树状数组的三个用途,现在说一下树状数组求 第K大。
对于一个数组,求第K大的数,只需要sort排序一下,取第k个值即可,但是当不停的改变数组中元素时(即在线询问),对于每次询问的第k大值,都要对数组重新进行排序,时间复杂度(快排N*logN)*(询问N);
那我们来看一下用树状数组:
首先树状数组C【i】里面存的是在i管辖的范围内各个组数的和,比如 1出现2次,2出现3次,4出现6次,那么a【1】=2,a【2】=3,a【3】=0,a【4】=6;故c【4】=11;
对于a数组{1,1,2,2,2,2,3,4,5,5,5,5,6,7,8};
则现在的a数组为{2,4,1,1,4,1,1,1},有这个数组,树状数组应该自己能构建出来了吧。。。
第一种方法:二分法
每次二分产生一个mid,判断get_sum(mid)的值与k的大小,更新mid的值,代码如下:
#include<stdio.h>#define N 200005int m,n;int f[N],a[N],c[N];void init()//初始化{ for(int i=1;i<N;i++) { f[i]=i; a[i]=1;//初始状态每种编号的猫只有一只 }}int find(int x)//判断两堆猫是否在一个集合{ if(x==f[x]) return x; return f[x]=find(f[x]);}int lowbit(int x) {return x&(-x);}void insert(int x,int v)//更改某种数量猫的个数{ while(x<N) { c[x]+=v; x+=lowbit(x); }}int get_sum(int x)//求和{ int sum=0; while(x>0) { sum+=c[x]; x-=lowbit(x); } return sum;}int main(){ int m,n; while(~scanf("%d%d",&n,&m)) { init(); insert(1,n);//个数为1的堆数有N堆,故在1处插入N,向后更新 int num=n; for(int i=1;i<=m;i++) { int mark; scanf("%d",&mark); if(mark==0) { int x,y; scanf("%d%d",&x,&y); int fx=find(x); int fy=find(y); if(fx==fy) continue; insert(a[fx],-1);//当fx和fy合并后,数量为fx和fy的堆数都减少1 insert(a[fy],-1); a[fy]+=a[fx]; insert(a[fy],1);//数量为fx+fy的堆数增加1 f[fx]=fy; num--;//没次合并总堆数-1 } else { int k; scanf("%d",&k); k=num-k+1;//第k大即第num-k+1小 int s=1,e=n,mid; while(s<=e) { mid=(s+e)/2; if(get_sum(mid)>=k)//尽量向左逼近 e=mid-1; else s=mid+1; } printf("%d\n",s); } } } return 0;}
上面的方法还比较好理解,下面说一下第二种方法:
首先说一个东西:任何一个正整数都能分成几个数的平方的和,然后就有了下面的方法:
前k大的数我们假如有x个,那么我们就先找到一个比x小的最大的数y,然后x=x-y,然后再找一个y,不停的找下去,直到x为1 。。。
下面解释摘自大牛博客:
因为要求第k小的数,所以要从左往右加过去,
上述过程其实就是把树状数组的求和操作逆向,从左往右求和,
边求和边判断控制范围内比当前值要小的数是否超过或等于k,如果是则跳回兄弟节点(ans-=(1<<i))
如8+4=12,假如12不满足要求,则重新变回8,下一次就加2,8+2=10,即跳到10控制的位置
上述累加过程不会重复计算,因为
比如15=8+4+2+1,数字依次为8 12 14 15,每次累加后的值都与前面的值无关,i小于其二进制末尾0的个数
即c[8] 、c[12] 、c[14]、 c[15]相加的话不会重复计算,再如11=8+2+1;数字依次为8 10 11,c[8],c[10],c[11]
各自控制着自己的范围,不会重复累加,所以就可以用cnt来累加前面的结果,最后cnt+c[ans]就表示了值<=ans的个数
简言之:上述的各个数字两两间控制的范围不会相交
代码如下:
#include<stdio.h>#define N 200005int m,n;int f[N],a[N],c[N];void init(){ for(int i=1;i<N;i++) { f[i]=i; a[i]=1; }}int find(int x){ if(x==f[x]) return x; return f[x]=find(f[x]);}int lowbit(int x) {return x&(-x);}void insert(int x,int v){ while(x<N) { c[x]+=v; x+=lowbit(x); }}int get_sum(int x){ int sum=0; while(x>0) { sum+=c[x]; x-=lowbit(x); } return sum;}int find_kth(int k){ int ans=0,cnt=0; for(int i=20;i>=0;i--)//1<<20应该不小了吧 { ans+=(1<<i); if(ans>N||cnt+c[ans]>=k) //这里大于等于k的原因是可能大小为ans的数不在c[ans]的控制范围之内,所以这里求的是 < k ans-=(1<<i); else //cnt用来累加比当前ans小的总组数 cnt+=c[ans]; } //求出的ans是累加和(即小于等于ans的数的个数)小于k的情况下ans的最大值,所以ans+1就是第k大的数 return ans+1;}//因为要求第k小的数,所以要从左往右加过去,//上述过程其实就是把树状数组的求和操作逆向,从左往右求和,//边求和边判断控制范围内比当前值要小的数是否超过或等于k,如果是则跳回兄弟节点(ans-=(1<<i))//如8+4=12,假如12不满足要求,则重新变回8,下一次就加2,8+2=10,即跳到10控制的位置//上述累加过程不会重复计算,因为//比如15=8+4+2+1,数字依次为8 12 14 15,每次累加后的值都与前面的值无关,i小于其二进制末尾0的个数//即c[8] 、c[12] 、c[14]、 c[15]相加的话不会重复计算,再如11=8+2+1;数字依次为8 10 11,c[8],c[10],c[11]//各自控制着自己的范围,不会重复累加,所以就可以用cnt来累加前面的结果,最后cnt+c[ans]就表示了值<=ans的个数//简言之:上述的各个数字两两间控制的范围不会相交int main(){ int m,n; while(~scanf("%d%d",&n,&m)) { init(); insert(1,n); int num=n; for(int i=1;i<=m;i++) { int mark; scanf("%d",&mark); if(mark==0) { int x,y; scanf("%d%d",&x,&y); int fx=find(x); int fy=find(y); if(fx==fy) continue; insert(a[fx],-1); insert(a[fy],-1); a[fy]+=a[fx]; insert(a[fy],1); f[fx]=fy; num--; } else { int k; scanf("%d",&k); k=num-k+1; printf("%d\n",find_kth(k)); } } } return 0;}
品味。。。
- poj_2985 The k-th Largest Group 树状数组求第K大
- poj 2985 The k-th Largest Group 并查集+树状数组求第k大
- poj2985 The k-th Largest Group 【树状数组求第K大】
- POJ 2985:The k-th Largest Group 树状数组求第K小的元素
- POJ 2985 The k-th Largest Group 第k大数 Treap / 树状数组 + 并查集
- POJ 2985The k-th Largest Group 线段树求整体第K大
- 树状数组从前往后求和,用来解第k大(或小)的数 poj 2985 The k-th Largest Group
- 树状数组从前往后求和,用来解第k大(或小)的数 poj 2985 The k-th Largest Group
- POJ2985 The k-th Largest Group(平衡树查询第K大)
- poj2985(名次树(treap))找第k大 The k-th Largest Group
- POJ 题目2985 The k-th Largest Group(线段树单点更新求第k大值,并查集)
- poj 2985 The k-th Largest Group 求第K大数 Treap, Binary Index Tree, Segment Tree
- poj 2985 The k-th Largest Group (并查集x全局动态第k大)
- POJ 2985 The k-th Largest Group
- POJ2985 The k-th Largest Group
- Poj 2985 The k-th Largest Group
- POJ-2985-The k-th Largest Group
- POJ 2985 The k-th Largest Group
- zk框架 官方文档翻译1
- SOCKET API和TCP STATE的对应关系__三次握手(listen,accept,connect)__四次挥手close及TCP延迟确认(调用一次setsockopt函数,设置TCP_QUI
- Kvm教程
- sql-isnull方法的使用
- 第四章作业
- poj_2985 The k-th Largest Group 树状数组求第K大
- java.util.Date和java.sql.Date的区别和相互转化
- C语言技巧:怎样把数组作为参数传递给函数?
- 设置MySQL5.6的远程连接
- JMF(Java多媒体框架)资料汇总
- ArcGIS Engine效率探究——要素的添加和删除、属性的读取和更新
- 网络请求应该是HttpUtil 工具类
- 指针A==B?
- 不用乘法或加法增加8 倍。现在用同样的方法增加7 倍。