POJ--2985 The k-th Largest Group(第K大组,带权并查集+树状数组+二分)
地址:http://poj.org/problem?id=2985
题意:
输入n,m。共n只猫,m组询问。
1,L,R。L号猫所在组和R号猫所在组合并
0,x。查询第x大组有几只猫。
解析:
理解了好久的说。。。最最重要的还是树状数组的本质要理解好,要不代码写再多也没用。
树状数组:c[i]表示所含人数在[i-lowbit(i)+1,i]的组有几个
hav[i]:表示i号猫所在组有几只猫
pr[i]:并查集
first:初始化:此代码是非常有利于理解这道题的。意思是:含有人数为1的组数有n组。
update(1,n);
1:当两个组L和R合并时,那么人数为hv[L]和hv[R]的组数都要-1。新组人数为:hv[L]+hv[R],+1。而且总组数--
update(hv[f1],-1); update(hv[f2],-1); update(hv[f1]+hv[f2],1); pr[f2]=f1; hv[f1]+=hv[f2]; all--;
2:询问时,把第k大转化为第n-k+1小。
接下来,就是二分了。md=(L+R)>>1。
getsum(md):人数为[md-lowbit(md)+1,md]的组有几个?是不是接近n-k+1?
以此类推~直到找到答案
总的AC代码,cin会超时
#include<iostream> #include<cstdio> #include<map> using namespace std; typedef long long ll; const int maxn=2e5+10; int pr[maxn],hv[maxn],c[maxn]; int n,m; int find(int x) { if(x!=pr[x]) return pr[x]=find(pr[x]); return x; } int lowbit(int x){ return x&(-x); } void update(int id,int x) { for(int i=id;i<=maxn;i+=lowbit(i)) c[i]+=x; } int getsum(int x) { int ans=0; for(int i=x;i>=1;i=i-lowbit(i)) ans+=c[i]; return ans; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { pr[i]=i; hv[i]=1; } update(1,n); int all=n; while(m--) { int x; scanf("%d",&x); if(x==0) { int l,r; scanf("%d%d",&l,&r); int f1=find(l); int f2=find(r); if(f1==f2) continue; update(hv[f1],-1); update(hv[f2],-1); update(hv[f1]+hv[f2],1); pr[f2]=f1; hv[f1]+=hv[f2]; all--; } else { int y; scanf("%d",&y); y=all-y+1; int l=1,r=n; while(l<=r) { int md=(l+r)>>1; if(getsum(md)>=y) r=md-1; else l=md+1; // cout<<l<<"-"<<r<<endl; } cout<<l<<endl; } } }