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

时间: 2024-09-28 21:00:12

POJ2985 并查集+线段树 求第k大的数的相关文章

UVA1455 - Kingdom(并查集 + 线段树)

UVA1455 - Kingdom(并查集 + 线段树) 题目链接 题目大意:一个平面内,给你n个整数点,两种类型的操作:road x y 把city x 和city y连接起来,line fnum (浮点数小数点一定是0.5) 查询y = fnum这条直线穿过了多少个州和city.州指的是连通的城市. 解题思路:用并查集记录城市之间是否连通,还有每一个州的y的上下界.建立坐标y的线段树,然后每次运行road操作的时候,对范围内的y坐标进行更新:更新须要分三种情况:两个州是相离,还是相交,还是包

《数据结构与算法分析:C语言描述》读书笔记------练习1.1 求第K大的数

求一组N个数中的第k个最大者,设k=N/2. 1 import java.util.Random; 2 3 4 public class K_Max { 5 6 /** 7 * @param args 8 */ 9 //求第K大的数,保证K大于等于1,小于等于array.length/2哦 10 public static int TopK(int array[],int K) 11 { 12 int topk[] = new int [K]; 13 for(int i = 0; i<topk.

普林斯顿公开课 算法3-2:求第k大的数

问题 给定N个元素的数组,求第k大的数. 特例 当k=0时,就是求最大值,当k=N-1时,就是求最小值. 应用 顺序统计 求top N排行榜 基本思想 使用快速排序方法中的分区思想,使得a[k]左侧没有更小的数,右侧没有更大的数 性能 快速选择算法的复杂度是N. 最坏情况下的复杂度是1/2N^2,但是可以通过预先洗牌来防止出现最坏情况 代码 public class QuickSort { // 对区间 [start, end) 进行分区 public static int partition(

Codeforces Gym 101194G Pandaria (2016 ACM-ICPC EC-Final G题, 并查集 + 线段树合并)

题目链接  2016 ACM-ICPC EC-Final Problem G 题意  给定一个无向图.每个点有一种颜色. 现在给定$q$个询问,每次询问$x$和$w$,求所有能通过边权值不超过w的边走到$x$的点的集合中,哪一种颜色的点出现的次数最多. 次数相同时输出编号最小的那个颜色.强制在线. 求哪种颜色可以用线段树合并搞定. 关键是这个强制在线. 当每次询问的时候,我们先要求出最小生成树在哪个时刻恰好把边权值不超过$w$的边都用并查集合并了. 在做最小生成树的时候每合并两个节点,另外开一个

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

用并查集记录每个联通块的根节点,每个联通块建一棵线段树,合并时合并线段树就可以了. 代码: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 #define N 100010 6 struct node{ 7 int l,r,x; 8 }c[N*20]; 9 int i,j,k,n,m,l,r,a[N],x,y,Rt[N],Num,b[N],f[N],

uva 1455 - Kingdom(并查集+线段树)

题目链接:uva 1455 - Kingdom 题目大意:平面上又n个城市,初始时城市之间没有任何双向道路相连,要求一次执行指令. road A B :在城市A和城市B之间连接一条双向道路 line C:询问一条y=C的水平线上穿过多少州和这些州总共有多少城市. 一个联通分量算一个州,C保证为小数部分为0.5的实数. 解题思路:线段树维护每个位置上州和城市的个数,并查集维护哪些城市属于同一个州,并且要记录这些州上下范围.每次新建一条道路,要相应根据两个州的y坐标范围对线段树进行维护. #incl

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

传送门 一看到第k大就肯定要想到什么权值线段树,主席树,平衡树之类的 然后就简单了 用并查集判断连通,每个节点建立一颗权值线段树,连通的时候直接合并即可 查询时再二分递归地查找 时间复杂度好像不是很稳定...但hzwer都用这种方法水过.. 正解好像是平衡树+启发式合并,以后学TT #include <cstdio> #include <iostream> #define N 100001 int n, m, q, cnt; int a[N], f[N], sum[N * 20],

并查集 + 线段树 LA 4730 Kingdom

题目传送门 题意:训练指南P248 分析:第一个操作可以用并查集实现,保存某集合的最小高度和最大高度以及城市个数.运用线段树成端更新来统计一个区间高度的个数,此时高度需要离散化.这题两种数据结构一起使用,联系紧密. #include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; const int M = 3 * N; const int INF = 0x3f3f3f3f; struct Point { int x, y

uvalive 4730王国kingdom(并查集+线段树)

 题意:有T组测试数据,每组数据的N表示有N个城市,接下来的N行里每行给出每个城市的坐标(0<=x,y<=1000000),然后有M(1<M<200000)个操作,操作有两类,(1)"road A B",表示将城市A和城市B通过一条道路连接,如果A和B原来属于不同的城市群,经过这个操作,A和B就在一个城市群里了,保证每条道路不会和其他道路相交(除了端点A和B).(2)"line C",表示查询当穿过y=C的直线,有多少个城市群.这几个城市