【BZOJ2733】永无乡(线段树合并)

题意:支持合并,求块内K小数

对于 100%的数据 n≤100000,m≤n,q≤300000

思路:对于每一个块建立一棵动态开点的线段树,暴力(启发式?)合并后二分下就行了

merge用函数的方式写因为懒得讨论x,y其中一个为0的情况,反正是把节点y并到x上

为什么这么暴力都不T?大概是因为随机数据的块的大小太平均了吧

var t:array[0..2100000,0..1]of longint;
    sum:array[0..2100000]of longint;
    fa,a,root:array[0..300000]of longint;
    n,m,x,y,k,i,j,s,cnt,p,q:longint;
    ch:string;

function find(k:longint):longint;
begin
 if fa[k]<>k then fa[k]:=find(fa[k]);
 exit(fa[k]);
end;

procedure pushup(x:longint);
var l,r:longint;
begin
 l:=t[x,0]; r:=t[x,1];
 sum[x]:=sum[l]+sum[r];
end;

procedure update(l,r,x:longint;var p:longint);
var mid:longint;
begin
 if p=0 then
 begin
  inc(cnt); p:=cnt;
 end;
 if l=r then
 begin
  inc(sum[p]); exit;
 end;
 mid:=(l+r)>>1;
 if x<=mid then update(l,mid,x,t[p,0])
  else update(mid+1,r,x,t[p,1]);
 pushup(p);
end;

function merge(x,y:longint):longint;
var mid:longint;
begin
 if (x=0)or(y=0) then exit(x+y);
 t[x,0]:=merge(t[x,0],t[y,0]);
 t[x,1]:=merge(t[x,1],t[y,1]);
 pushup(x);
 exit(x);
end;

function query(l,r,k,p:longint):longint;
var mid,tmp:longint;
begin
 if sum[p]<k then exit(-1);
 if l=r then exit(a[l]);
 tmp:= sum[t[p,0]];
 mid:=(l+r)>>1;
 if tmp>=k then exit(query(l,mid,k,t[p,0]))
  else exit(query(mid+1,r,k-tmp,t[p,1]));
end;

begin
 assign(input,‘bzoj2733.in‘); reset(input);
 assign(output,‘bzoj2733.out‘); rewrite(output);
 readln(n,m);
 for i:=1 to n do
 begin
  read(x); a[x]:=i;
  update(1,n,x,root[i]);
 end;
 for i:=1 to n do fa[i]:=i;
 for i:=1 to m do
 begin
  readln(x,y);
  p:=find(x); q:=find(y);
  if p<>q then
  begin
   fa[q]:=p;
   merge(root[p],root[q]);
  end;
 end;
 readln(m);
 for i:=1 to m do
 begin
  readln(ch);
  s:=0; x:=0; y:=0; k:=length(ch);
  for j:=2 to k do
  begin
   if ch[j]=‘ ‘ then begin inc(s); continue; end;
   if s=1 then x:=x*10+ord(ch[j])-ord(‘0‘);
   if s=2 then y:=y*10+ord(ch[j])-ord(‘0‘);
  end;
  case ch[1] of
   ‘Q‘:writeln(query(1,n,y,root[find(x)]));
   ‘B‘:
   begin
    p:=find(x); q:=find(y);
    if p<>q then
    begin
     fa[q]:=p; root[p]:=merge(root[p],root[q]);
    end;
   end;
  end;
 end;

 close(input);
 close(output);
end.
时间: 2024-10-25 18:21:53

【BZOJ2733】永无乡(线段树合并)的相关文章

【bzoj2733】[HNOI2012]永无乡 线段树合并

Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k

bzoj2733 永无乡 splay树的启发式合并

https://vjudge.net/problem/HYSBZ-2733 给一些带权点,有些点是互相连通的, 然后给出2种操作,在两点间加一条边,或者询问一个点所在的连通块内的第k小值的编号 并查集辅助+splay的启发式合并就行 由于结构简单,动态开点线段树合并也可以做 我写的是splay,由于一个奇怪的bug,我一气之下把之前的核心代码里的我自己写的splay和rotate代码全换成板子了 #include <bits/stdc++.h> #define endl '\n' #defin

【BZOJ2733】永无乡[splay启发式合并or线段树合并]

题目大意:给你一些点,修改是在在两个点之间连一条无向边,查询时求某个点能走到的点中重要度第k大的点.题目中给定的是每个节点的排名,所以实际上是求第k小:题目求的是编号,不是重要度的排名.我一开始差点被这坑了. 网址:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 这道题似乎挺经典的(至少我看许多神犇很早就做了这道题).这道题有两种写法:并查集+(splay启发式合并or线段树合并).我写的是线段树合并,因为--splay不会打+懒得学.

BZOJ2733 永无乡【splay启发式合并】

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含

[BZOJ2733] [HNOI2012] 永无乡 (splay启发式合并)

Description 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达另一个岛.如果从岛 a 出发经过若干座(含 0 座)桥可以到达岛 b,则称岛 a 和岛 b 是连 通的.现在有两种操作:B x y 表示在岛 x 与岛 y 之间修建一座新桥.Q x k 表示询问当前与岛 x连通的所有岛中第 k 重要的是哪座岛,即所有与岛 x 连通的岛中重要度排名第 k

[bzoj2733]永无乡&amp;&amp;[bzoj3545]Peaks

并不敢说完全会了线段树合并,只是至少知道原理写法了...还是太菜了,每天被大佬吊锤qwq 我看到的几道线段树合并都是权值线段树的合并.这个算法适用范围应该只是01线段树的. 这两道算入门题了吧... 发现粘题面没人看(自己都懒得看),以后粘链接加题意吧. 永无乡 给$n$个没有连边的带权点,动态加边,询问$u$所在连通块权值第$k$大的点是什么.$n \leq 1e5 , q\leq 3e5$ 离线永无乡?? 给定森林,点有点权有重复!,边有边权.询问$u$所在连通块,只能走边权小于$w$的边,

BZOJ 2733: [HNOI2012]永无乡(treap + 启发式合并 + 并查集)

不难...treap + 启发式合并 + 并查集 搞搞就行了 ---------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep(i, n) for(int i = 0; i &l

bzoj2733 永无乡 平衡树按秩合并

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2733 题意:动态连边,求出某个联通块中权值第$k$小的点. 首先,看到名次果断想平衡树……查询这个问题很好解决,但是合并……恐怕只能暴力修改了吧…… 这时候我们需要一个武器:启发式合并,通俗的讲就是小的插到大的里面去突然污了起来.我们可以想象一下,如果把大的那棵树合并到小的那棵去,那么每个节点暴力合并……时间代价不堪设想……因此按秩合并可以有效减短合并时间……然后就是暴力插点删点就行了……

BZOJ 2733 [HNOI2012]永无乡(启发式合并+Treap+并查集)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2733 [题目大意] 给出n个点,每个点都有自己的重要度,现在有连边操作和查询操作, 查询操作要求找出一个连通块中重要度第k的点的id [题解] 我们用Treap维护每个连通块,对于连边操作,我们用启发式合并, 将size比较小的Treap并入size比较大的Treap,同时用并查集维护连通信息 [代码] #include <cstdio> #include <algorith

BZOJ 2733 HNOI 2012 永无乡 平衡树启发式合并

题目大意:有一些岛屿,一开始由一些无向边连接.后来也有不断的无向边加入,每一个岛屿有个一独一无二的重要度,问任意时刻的与一个岛屿联通的所有岛中重要度第k大的岛的编号是什么. 思路:首先连通性一定要用并查集维护,然后就是联通快内的第k大问题,显然是平衡树.但是并查集的合并怎么搞?可以考虑按秩合并,这样的话就保证每次在平衡树中处理的元素尽量的少,就可以水过这个题了. 注意一下输出-1的判断. CODE: #include <map> #include <cstdio> #include