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;
}
View Code

 

以上是关于POJ2985 并查集+线段树 求第k大的数的主要内容,如果未能解决你的问题,请参考以下文章

POJ--2985 The k-th Largest Group(第K大组,带权并查集+树状数组+二分)

POJ--2985 The k-th Largest Group(第K大组,带权并查集+树状数组+二分)

poj2985

POJ2985 The k-th Largest Group

bzoj2733 [ HNOI2012 ] -- 并查集+线段树合并

[BZOJ2733] [HNOI2012]永无乡(并查集 + 线段树合并)