bzoj3110: [Zjoi2013]K大数查询 【树套树,标记永久化】

好久没写题解了。

但是这题太神了然后做法太神了于是写一下。

这题做法很多,比如黄学长hzw的权值线段树套线段树,比如学长云的bit套主席树(其实是写法更神然后我不会用)。

然后看到hzhwcmhf大神题解。

http://tieba.baidu.com/p/2246783535

震惊了。

好了开说说做法。建一颗朴素的线段树,树的每个点表示每个区间,然后每个区间建两棵树,一棵是mark树,一棵是all树,两棵都是权值线段树。

“mark表示该区间每个点上都会加上mark线段树里的元素
 all表示该区间所有点的元素集合”

这道题麻烦的地方就在与标记,如果标记要下传的话,那么每次可能要下传很多个点。

于是标记永久化,就是不要下传。

对于修改:如果修改区间是当前点区间,那么就加入mark中。如果不是,那么就加入all。操作都是单点修改。加入mark时为c+1,加入all是为c+len

对于询问:首先包含在查询区间内的区间的所有的all,然后mark要去掉没有重合在一起的地方,乘回差值。

(太久没写题解都不知道怎么说了)

速度还是挺快的

type
  arr=record
    all,mark:longint;
  end;
const
  maxn=10000000;
var
  tree:array[0..maxn]of arr;
  size,lson,rson,root1,root2:array[0..maxn]of longint;
  tot,sum,n,m,i,j,k,l:longint;

function add:longint;
begin
  inc(tot);
  exit(tot);
end;

procedure sinsert(var x:longint;y,z:longint);
var
  left,right,mid,old:longint;
begin
  if x=0 then x:=add;
  old:=x;
  left:=1;
  right:=n;
  while left<=right do begin
    mid:=(left+right)>>1;
    inc(size[x],z);
    if left=right then break;
    if y<=mid then begin
      right:=mid;
      if lson[x]=0 then lson[x]:=add;
      x:=lson[x];
    end
    else begin
      left:=mid+1;
      if rson[x]=0 then rson[x]:=add;
      x:=rson[x];
    end;
  end;
  x:=old;
end;

procedure binsert(x,l,r,tl,tr,tc:longint);
var
  mid:longint;
begin
  if (tl=l) and (tr=r) then begin
    sinsert(tree[x].mark,tc,1);
    exit;
  end;
  mid:=(l+r)>>1;
  sinsert(tree[x].all,tc,tr-tl+1);
  if tl>mid then binsert(x<<1+1,mid+1,r,tl,tr,tc)
  else
    if tr<=mid then binsert(x<<1,l,mid,tl,tr,tc)
    else begin
      binsert(x<<1,l,mid,tl,mid,tc);
      binsert(x<<1+1,mid+1,r,mid+1,tr,tc);
    end;
end;

procedure before(x,l,r,tl,tr:longint);
var
  mid:longint;
begin
  inc(sum);
  root1[sum]:=tree[x].mark;
  root2[sum]:=tr-tl+1;
  if (tl=l) and (tr=r) then begin
    inc(sum);
    root1[sum]:=tree[x].all;
    root2[sum]:=1;
    exit;
  end;
  mid:=(l+r)>>1;
  if tl>mid then before(x<<1+1,mid+1,r,tl,tr)
  else
    if tr<=mid then before(x<<1,l,mid,tl,tr)
    else begin
      before(x<<1,l,mid,tl,mid);
      before(x<<1+1,mid+1,r,mid+1,tr);
    end;
end;

procedure query(x,y,z:longint);
var
  left,right,now,mid:longint;
begin
  sum:=0;
  before(1,1,n,x,y);
  left:=1;
  right:=n;
  while left<right do begin
    now:=0;
    mid:=(left+right)>>1;
    for i:=1 to sum do
      now:=size[rson[root1[i]]]*root2[i]+now;
    if now<z then begin
      dec(z,now);
      right:=mid;
      for i:=1 to sum do
        root1[i]:=lson[root1[i]];
    end
    else begin
      left:=mid+1;
      for i:=1 to sum do
        root1[i]:=rson[root1[i]];
    end;
  end;
  writeln(left);
end;

begin
  readln(n,m);
  while m>0 do begin
    dec(m);
    readln(i,j,k,l);
    if i=1 then binsert(1,1,n,j,k,l)
      else query(j,k,l);
  end;
  readln;
  readln;
end.

数组开小竟然wa了两次。

时间: 2024-10-17 16:51:05

bzoj3110: [Zjoi2013]K大数查询 【树套树,标记永久化】的相关文章

[BZOJ3110] [Zjoi2013] K大数查询 (树套树)

Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Output 输出每个询问的结果 Sample Input 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 Sample Output 1 2 1 HINT [

【bzoj3110】[Zjoi2013]K大数查询 整体二分+树状数组区间修改

题目描述 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c.如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. 输入 第一行N,M接下来M行,每行形如1 a b c或2 a b c 输出 输出每个询问的结果 样例输入 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3 样例输出 1 2 1 题解 整体二分+树状数组区间修改 当年naive的树套树题解 前两天由于要

bzoj3110: [Zjoi2013]K大数查询

喜闻乐见的简单树套树= =第一维按权值建树状数组,第二维按下标建动态开点线段树,修改相当于第二维区间加,查询在树状数组上二分,比一般的线段树还短= =可惜并不能跑过整体二分= =另外bzoj上的数据有负数= =额其他树套树方法也是可以的爱怎么套怎么套= = #include<cstdio> #define J (i+j>>1) #define I (J+1) typedef unsigned ll; const int N=1e5+5; ll n,m,q,i,j,k,l,s,t,v

BZOJ3110 ZJOI2013 K大数查询 线段树套线段树

题意:给定一个数列,维护:1.在a和b之间插入c  2.询问[a,b]中的第c大 题解: 权值线段树套区间线段树 外层的权值线段树中每个节点如果维护[L,R]这个区间,那么该节点所对应的线段树维护的就是[L,R]这些数在每个区间里出现了几次,也就是说如果外层线段树的某个节点维护[L,R],其所对应的内层线段树中某个节点[l,r]维护的值就是[L,R]这些数在[l,r]这个区间中出现的次数. 最后吐槽一下动态内存+指针版线段树MLE……尼玛我写指针版完全习惯了根本就不会写数组版了QAQ,自己拿数据

BZOJ3110 [ZJOI2013] K大数查询(加强数据)

原来的题解:http://www.cnblogs.com/jimzeng/p/bzoj3110.html 有必要特意再写一篇题解…… OrzKPM!KPM加了两组数据结果我原来的代码就被叉了…… 看到数据没有负数KPM就加了负数,然后还卡了long long(极端情况:50000次,每次在1,50000中加入一个同样的数) 需要离散化数据,加long long 然后速度就明显慢了……(9556ms -> 12292ms) 贴代码: 1 #include <iostream> 2 #inc

【BZOJ3110】[Zjoi2013]K大数查询 树套树

[BZOJ3110][Zjoi2013]K大数查询 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c,如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b c Output 输出每个询问的结果 Sample Input 2 5 1 1 2 1 1 1 2 2 2 1 1 2 2 1 1 1 2 1 2 3

BZOJ 3110: [Zjoi2013]K大数查询 [树套树]

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6050  Solved: 2007[Submit][Status][Discuss] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a

3110: [Zjoi2013]K大数查询 树状数组套线段树

3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1384  Solved: 629[Submit][Status] Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M接下来M行,每行形如1 a b c或2 a b

树套树专题——bzoj 3110: [Zjoi2013] K大数查询 &amp; 3236 [Ahoi2013] 作业 题解

[原题1] 3110: [Zjoi2013]K大数查询 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 978  Solved: 476 Description 有N个位置,M个操作.操作有两种,每次操作如果是1 a b c的形式表示在第a个位置到第b个位置,每个位置加入一个数c 如果是2 a b c形式,表示询问从第a个位置到第b个位置,第C大的数是多少. Input 第一行N,M 接下来M行,每行形如1 a b c或2 a b c Outpu