POJ2985 并查集+线段树 求第k大的数
Posted iEdson
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了POJ2985 并查集+线段树 求第k大的数相关的知识,希望对你有一定的参考价值。
其实这题之前做过,线段树一直不熟,所以也一直没有搞懂
本题的关键是线段树原始区间代表的是每一种容器(size不同)的数量
比如 刚开始都是互不相关的,所以1的容器有n个 2 3 4。。。为0个
线段树每个结点的附加信息是该区间的和
本题查找出的代码是关键 比如左右子树分别为sum 27 25 ,则第26大的容器必然在左子树上,继续递归下去,则要在该左子树找 (26-25)大的容器。。。
通俗讲 本题就是改变点修改 求和变化(附加信息)的情况 只是用k大转化了一下
线段树还是做的太少,近期还要加强专门训练
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cmath> #include <stack> #include <string> #include <queue> #include <vector> #include <algorithm> #include <ctime> using namespace std; //#define EdsonLin #ifdef EdsonLin #define debug(...) fprintf(stderr,__VA_ARGS__) #else #define debug(...) #endif //EdsonLin typedef long long ll; typedef double db; const int inf = 0x3f3f3f; const int MAXN = 2e5+10; const int MAXNN = 2e6+100; //const int MAXM = 1e6; //const int MAXM = 3e4+100; const int MOD = 1000000007; const db eps = 1e-3; #define PB push_back int a[MAXN],p[MAXN]; int n,m,k; int readint(){int x;scanf("%d",&x);return x;} int Find(int x){return (x==p[x]?x:Find(p[x]));} void Union(int u,int v){p[u] = Find(u);p[v] = Find(v);p[p[v]] = p[u];} void init(){ for(int i=1;i<=n;i++){ a[i] = 1; p[i] = i; } } struct sT{ int sum[MAXN*3]; int n; void popup(int o){ int lc = o*2; int rc = o*2|1; sum[o] = sum[lc] + sum[rc]; } void build(int o,int lc,int rc){ if(lc==1)sum[o] = n; else sum[o] = 0; if(lc==rc)return; if(lc<rc){ int mc = lc + (rc-lc)/2; build(o*2,lc,mc); build(o*2|1,mc+1,rc); } } void update(int o,int lc,int rc,int val,int c){ int mc = lc + (rc-lc)/2; if(lc==rc&&lc==val){sum[o] += c;return;} if(lc==rc)return; if(mc<val)update(o*2|1,mc+1,rc,val,c); //此步容易错误 改变所有和val有关节点的信息 else update(o*2,lc,mc,val,c); popup(o); //向上递归,改变父节点附加信息 } void query(int o,int lc,int rc,int k){ if(lc==rc){ printf("%d\\n",lc); return; } int mc = lc+(rc-lc)/2; if(k<=sum[o*2|1])query(o*2|1,mc+1,rc,k); else query(o*2,lc,mc,k-sum[o*2|1]); } void init(){ memset(sum,0,sizeof(sum)); this->n = n; }; }solver; int main() { while(cin>>n>>m){ init(); solver.init(); solver.build(1,1,n); for(int i=0;i<m;i++){ int sg; scanf("%d",&sg); if(!sg){ int u,v; scanf("%d%d",&u,&v); u = Find(u); v = Find(v); if(u==v)continue; /*Union(u,v);*/ p[v] = u; solver.update(1,1,n,a[u],-1); solver.update(1,1,n,a[v],-1); solver.update(1,1,n,a[u]+a[v],1); a[u] += a[v]; }else{ scanf("%d",&k); solver.query(1,1,n,k); } } } //cout << "Hello world!" << endl; return 0; }
以上是关于POJ2985 并查集+线段树 求第k大的数的主要内容,如果未能解决你的问题,请参考以下文章
POJ--2985 The k-th Largest Group(第K大组,带权并查集+树状数组+二分)
POJ--2985 The k-th Largest Group(第K大组,带权并查集+树状数组+二分)
POJ2985 The k-th Largest Group