【poj2104-求区间第k大数(不修改)】主席树/可持续化线段树

第一道主席树~然而是道比较水的。。。因为它不用修改。。。

转载一个让我看懂的主席树的讲解吧:http://blog.csdn.net/regina8023/article/details/41910615 (未授权,侵权删)

---------------------------------------------------------------------------------------------------------------------

那么如果要询问i-j之间数字出现的次数怎么办呢?

因为每一棵线段树的区间都是相同的,所以要求l-r之间的数字的出现次数只要用前r位出现的次数减去前l-1位出现的次数,就是ans

但是如果有修改操作怎么办?

如果沿用上面的做法,那么修改操作是O(nlogn)的,查询是O(1)的,修改要花好长时间。。。

前缀和联想到了树状数组,那么将前缀和用树状数组维护的话修改是O(logn*logn),查询时O(logn),查询的时间虽然变长,但是修改的时间缩短许多!!

注意:

函数式线段树的数组要开大一点!!

---------------------------------------------------------------------------------------------------------------------

这题就是模版题啦,先离散,求区间第k大的时候lx=root[l-1],rx=root[r],两边同时走不断作差,看看左孩子的数量,如果k更大就减掉左孩子的到有右孩子中找。

代码:

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<iostream>
  6 #include<algorithm>
  7 using namespace std;
  8
  9 const int N=100010,INF=(int)1e9+100;
 10 struct trnode{
 11     int lc,rc,cnt;
 12 }t[30*N];
 13 struct node{
 14     int d,id;
 15 }p[N];
 16 int n,m,tl,mx;
 17 int a[N],num[N],root[N];
 18
 19 bool cmp(node x,node y){return x.d<y.d;}
 20
 21 int bt(int l,int r)
 22 {
 23     int x=++tl;
 24     // a[x].l=l;a[x].r=r;
 25     t[x].lc=t[x].rc=0;
 26     t[x].cnt=0;
 27     if(l<r)
 28     {
 29         int mid=(l+r)/2;
 30         t[x].lc=bt(l,mid);
 31         t[x].rc=bt(mid+1,r);
 32     }
 33     return x;
 34 }
 35
 36 int add(int rt,int x)
 37 {
 38     int now=++tl,tmp=now;
 39     t[now].cnt=t[rt].cnt+1;
 40     int l=1,r=mx,mid;
 41     while(l<r)
 42     {
 43         mid=(l+r)/2;
 44         if(x<=mid)
 45         {
 46             t[now].lc=++tl;
 47             t[now].rc=t[rt].rc;
 48             rt=t[rt].lc;
 49             now=tl;
 50             r=mid;
 51         }
 52         else
 53         {
 54             t[now].lc=t[rt].lc;
 55             t[now].rc=++tl;
 56             rt=t[rt].rc;
 57             now=tl;
 58             l=mid+1;
 59         }
 60         t[now].cnt=t[rt].cnt+1;
 61     }
 62     return tmp;
 63 }
 64
 65 int query(int lx,int rx,int k)
 66 {
 67     int l=1,r=mx,mid;
 68     while(l<r)
 69     {
 70         mid=(l+r)/2;
 71         if(t[t[rx].lc].cnt-t[t[lx].lc].cnt>=k)
 72         {
 73             r=mid;
 74             lx=t[lx].lc;
 75             rx=t[rx].lc;
 76         }
 77         else
 78         {
 79             l=mid+1;
 80             k-=t[t[rx].lc].cnt-t[t[lx].lc].cnt;
 81             lx=t[lx].rc;
 82             rx=t[rx].rc;
 83         }
 84     }
 85     return l;
 86 }
 87
 88 void output(int x)
 89 {
 90     printf("x = %d  lc = %d  rc = %d  cnt = %d\n",x,t[x].lc,t[x].rc,t[x].cnt);
 91     if(t[x].lc) output(t[x].lc);
 92     if(t[x].rc) output(t[x].rc);
 93 }
 94
 95 int main()
 96 {
 97     freopen("a.in","r",stdin);
 98     while(scanf("%d%d",&n,&m)!=EOF)
 99     {
100         tl=0;mx=0;
101         for(int i=1;i<=n;i++)
102         {
103             scanf("%d",&a[i]);
104             p[i].d=a[i];p[i].id=i;
105         }
106         sort(p+1,p+1+n,cmp);
107         p[0].d=INF;
108         for(int i=1;i<=n;i++)
109         {
110             if(p[i].d!=p[i-1].d) mx++,num[mx]=p[i].d;
111             a[p[i].id]=mx;
112         }
113         root[0]=bt(1,mx);
114         for(int i=1;i<=n;i++)
115             root[i]=add(root[i-1],a[i]);
116         // output(root[5]);
117         for(int i=1;i<=m;i++)
118         {
119             int l,r,k;
120             scanf("%d%d%d",&l,&r,&k);
121             printf("%d\n",num[query(root[l-1],root[r],k)]);
122         }
123     }
124     return 0;
125 }
时间: 2024-12-15 01:45:41

【poj2104-求区间第k大数(不修改)】主席树/可持续化线段树的相关文章

poj2104 求区间第k大 可持久化线段树

poj2104 求区间第k大  可持久化线段树 #include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef

[csu/coj 1080]划分树求区间前k大数和

题意:从某个区间内最多选择k个数,使得和最大 思路:首先题目给定的数有负数,如果区间前k大出现负数,那么负数不选和更大,于是对于所有最优选择,负数不会出现,所以用0取代负数,问题便转化为区间的前k大数和. 划分树: [1  6  3  8  5  4  7  2] [6  8  5  7][1  3  4  2] [8  7][6  5][3  4][1  2] [8][7][6][5][4][3][2][1] 把快排的结果从上至下依次放入线段树,就构成了划分树,划分的意思就是选定一个数,把原序

poj2104求区间第k小,静态主席树入门模板

看了很久的主席树,最后看https://blog.csdn.net/williamsun0122/article/details/77871278这篇终于看懂了 #include <stdio.h> #include<algorithm> using namespace std; typedef long long ll; const int maxn = 1e5+5; int T[maxn],L[maxn*20],R[maxn*20],sum[maxn*20]; //sz[]为原

POJ 2761 Feed the dogs(树状数组求区间第K大)

题目链接: 戳我 题目大意:Jiajia要为宠物狗,宠物狗按成一排站好(1 < i <= n),第 i 只狗的喜欢程度是 a[i], 之后他会先喂某个区间内第k个 即 n 个数, m个询问,接着是 n个数 接下来 m 行,每行是 l r k即 l 到 r 这个区间第 k 小的数,每个询问输出一个答案,即 a[i] 求区间第k大有很多算法, 详见此博客 [数据结构练习] 求区间第K大数的几种方法 我用的树状数组解法,来自 树状数组从前往后求和,用来解第k大(或小)的数 poj 2985 The

主席树的各类模板(区间第k大数【动,静】,区间不同数的个数,区间&lt;=k的个数)

取板粗 1.(HDOJ2665)http://acm.hdu.edu.cn/showproblem.php?pid=2665 (POJ2104)http://poj.org/problem?id=2104 (POJ2761)http://poj.org/problem?id=2761 题意:求区间第K大,主席树模板题 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; t

poj 2104主席树求区间第k小

POJ - 2104 题意:求区间第k小 思路:无修改主席树 AC代码: #include "iostream" #include "iomanip" #include "string.h" #include "stack" #include "queue" #include "string" #include "vector" #include "set&

区间第k大数

思路是分块,不然得树套树(我是蒟蒻不会) 用分块T2的思路+二分就能求出区间第k大数 代码还未交过,先放这里 #include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<vector> #include<cmath> using namespace std; vector<int>vec[1004]; int n,bl

POJ2761---Feed the dogs (Treap求区间第k大)

题意 就是求区间第k大,区间 不互相包含. 尝试用treap解决一下 第k大的问题. 1 #include <set> 2 #include <map> 3 #include <cmath> 4 #include <ctime> 5 #include <queue> 6 #include <stack> 7 #include <cstdio> 8 #include <string> 9 #include <

poj 2401 划分树 求区间第k大的数

题目:http://poj.org/problem?id=2104 划分树待我好好理解下再写个教程吧,觉得网上的内容一般,,, 模板题: 贴代码: #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; #define CLR(a) memset(a,0,sizeof(a)) const int MAXN = 1000