poj2985

树状数组求第k大值

思路有一点想ST表

将求第k大变为求第(tot-k+1)小的数,tr[i]统计小于等于i的数的总数

每次不断逼近,若当前[ans,ans+(1<<i)]在范围内,则加上,否则直接到下一次循环

过程中的一个地方要注意(详见代码注释)

#include<cstdio>
#include<cctype>
using namespace std;
int n,m,fa[200002],tr[200002],siz[200002];

inline void read(int &x){
    char ch=getchar();x=0;
    while(!isdigit(ch))ch=getchar();
    while(isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
} 

inline int findfa(int x){
    if(fa[x]==x)return x;return fa[x]=findfa(fa[x]);
}

inline void updata(int now,int ad){
    for(;now<=n;now+=now&-now)tr[now]+=ad;
}

inline int query(int k){
    int cnt=0,ans=0;
    for(int i=20;i>=0;i--){
        ans+=1<<i;
        if(ans>n||cnt+tr[ans]>=k)ans-=1<<i;else cnt+=tr[ans];//这里if语句中一定的是<=,否则若存在多个与k大数相同的数就会存在漏洞
    }
    return ans+1;
}

int main(){
    read(n);read(m);
    int tot=n;
    for(int i=1;i<=n;i++){siz[i]=1;fa[i]=i;}
    updata(1,n);
    while(m--){
        int c,x,y;
        read(c);
        switch(c){
            case 0:{
                read(x);read(y);int fx=findfa(x),fy=findfa(y);
                if(fx==fy)continue;
                updata(siz[fx],-1);updata(siz[fy],-1);
                siz[fy]+=siz[fx];updata(siz[fy],1);fa[fx]=fy;tot--;   
                break;} 
            case 1:read(x);printf("%d\n",query(tot-x+1));break;
        }
    }
}
原文地址:https://www.cnblogs.com/MikuKnight/p/8974079.html