SPOJ3267--D-query (主席树入门练习)

题意:查找区间内不同数字的个数。

两种做法,一种是 树状数组离线,另一种就是主席树。

树状数组离线操作的链接 http://www.cnblogs.com/oneshot/p/4110415.html

两种方法思路差不多,都是扫一遍,如果这个数曾经出现过那么就 在上次位置-1,如果没有出现过就在 当前位置+1,同时更新该数字的最新的位置。

这样的话,在主席树里面 以x为根的线段树存的就是1-x之间不同的数字的个数。我们只需要查找以r为根的线段树同时大于l的区间内的个数就行了。

给主席跪了,orz。主席树其实就是每个位置对应一颗线段树,但是这样 n 个点 n 棵线段树显然会MLE,怎么解决呢?我们可以看到 从第 i 棵线段树到第i+1 棵线段树,很多子树都是相同的,这样如果再重新建一棵完整的线段树显然浪费了很大的空间。我们只需要把第i棵线段树的 某个子树同时第i+1棵线段树 某个 节点下面即可,这样就大大减小了空间复杂度。

  1 #include <map>
  2 #include <cstdio>
  3 #include <vector>
  4 #include <cstdlib>
  5 #include <cstring>
  6 #include <algorithm>
  7 using namespace std;
  8 const int maxn = 3e4+10;
  9 int a[maxn],c[maxn*18],lson[maxn*18],rson[maxn*18],tot,n,m;
 10 int build(int l,int r)
 11 {
 12     int root = tot++;
 13     c[root] = 0;
 14     if (l != r)
 15     {
 16         int mid = (l + r) >> 1;
 17         lson[root] = build(l,mid);
 18         rson[root] = build(mid + 1,r);
 19     }
 20     return root;
 21 }
 22 int update(int root,int pos,int val)
 23 {
 24     int newroot = tot++;
 25     int tmp = newroot;
 26     c[newroot] = c[root] + val;
 27     int l = 1,r = n;
 28     while (l < r)
 29     {
 30         int mid = (l + r) >> 1;
 31         if (pos <= mid)
 32         {
 33             rson[newroot] = rson[root];
 34             lson[newroot] = tot++;
 35             newroot = lson[newroot];
 36             root = lson[root];
 37             r = mid;
 38         }
 39         else
 40         {
 41             lson[newroot] = lson[root];
 42             rson[newroot] = tot++;
 43             newroot = rson[newroot];
 44             root = rson[root];
 45             l = mid + 1;
 46         }
 47         c[newroot] = c[root] + val;
 48     }
 49     return tmp;
 50 }
 51 int query(int root,int pos)
 52 {
 53     int res = 0;
 54     int l = 1,r = n;
 55     while (pos > l)
 56     {
 57         int mid = (l + r) >> 1;
 58         if (pos <= mid)
 59         {
 60             res += c[rson[root]];
 61             root = lson[root];
 62             r = mid;
 63         }
 64         else
 65         {
 66             root = rson[root];
 67             l = mid + 1;
 68         }
 69     }
 70     return res + c[root];
 71 }
 72 int per_root[maxn];
 73 int main(void)
 74 {
 75     #ifndef ONLINE_JUDGE
 76         freopen("in.txt","r",stdin);
 77     #endif
 78     while (~scanf ("%d",&n))
 79     {
 80         tot = 0;
 81         for (int i = 1; i <= n ;i++)
 82             scanf ("%d",a+i);
 83         per_root[0] = build(1,n);
 84         map<int,int>mp;
 85         for (int i = 1; i <= n; i++)
 86         {
 87             if (mp.find(a[i]) == mp.end())
 88             {
 89                 per_root[i] = update(per_root[i-1],i,1);
 90             }
 91             else
 92             {
 93                 int tmp = update(per_root[i-1],mp[a[i]],-1);
 94                 per_root[i] = update(tmp,i,1);
 95             }
 96             mp[a[i]] = i;
 97         }
 98         scanf ("%d",&m);
 99         for (int i = 0; i < m; i++)
100         {
101             int l,r;
102             scanf ("%d%d",&l,&r);
103             printf("%d\n",query(per_root[r],l));
104         }
105     }
106     return 0;
107 }
时间: 2024-10-09 04:01:39

SPOJ3267--D-query (主席树入门练习)的相关文章

Poj 2104(主席树入门

题目:静态查询区间第k大. 主席树入门题目,之前看的很多资料一上来就是动态区间第k大,看得很费劲,后来找了个写得清晰的,感觉静态的还不算难,代码也不长. /* * @author: Cwind */ //#pragma comment(linker, "/STACK:102400000,102400000") #include <iostream> #include <map> #include <algorithm> #include <cs

hdu 5919 主席树入门题

主席树是从右往左初始化,每次把这个数出现过的位置消去,然后在当前位置加一. 然后我的做法是查两遍,第一遍能找出不同的个数,除一半:再用这个值查,一直到底,最后返回位置,比较套路的一题. #include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include &l

POJ 2104&amp;HDU 2665 Kth number(主席树入门+离散化)

K-th Number Time Limit: 20000MS   Memory Limit: 65536K Total Submissions: 50247   Accepted: 17101 Case Time Limit: 2000MS Description You are working for Macrohard company in data structures department. After failing your previous task about key inse

SPOJ3267 D-query(主席树模版)

题意: 给一个序列,问区间内有多少个不相同的数 思路: 主席树模版,按斌巨的模版写了一发orz /* *********************************************** Author :devil ************************************************ */ #include <cstdio> #include <cstring> #include <iostream> #include <al

[知识点]主席树入门 区间k值

入坑主席树主要是因为昨天考试的后两道题不可改2333 而且觉得这个还挺有用,于是果断入坑 不过蒟蒻的我只是在上午看了看大概思路,下午开的运动会没时间码,于是晚上码了出来.但是目前只会无修改的区间K值问题,加上又比较抽象,思路屡了半天才刚刚醒悟,于是写下来记录一下. 不扯那么多辣鸡套路,直接说思路打法(以k小值为例): 我们先要以权值建一颗线段树,然后每个节点记录的是这个节点管辖的范围内数列中数的个数. 我们需要建好多好多线段树.简单来讲呢,就是[1,1],[1,2]···[1,n]这么多颗,每棵

poj2104 K-th Number 主席树入门;

题目链接:K-th Number 题解:我们先把数组离散离散化一下,然后先不考虑L,R的区间的关系,我们有一个棵线段树sum[]保存的是第几大到第几大出现的个数,这样我们想要询问这颗线段数的第k大是多少可以在log(n)次下就找到,但是区间的不同,一颗线段树是解决不了的,那我们如何得到L,R区间的sum数组呢?.我们可以建N棵线段树,第一棵树是空树,然后第一个数过来我们再建一课线段树在原来树的基础上,加上这个数对sum数组的贡献,这样从第一个到第N个建N棵线段树建好,我们可以发现sum[]有前缀

[CF893F]Subtree Minimum Query (主席树)

题面: 传送门:http://codeforces.com/problemset/problem/893/F 题目大意:给你一颗有根树,点有权值,问你每个节点的子树中距离其不超过k的点的权值的最小值.(边权均为1,强制在线) Solution 这题很有意思. 我们一般看到这种距离不超过k的题目,第一反应一般是建以深度为下标,以dfs序为时间轴的的主席树. 很不幸,区间最小值并不能通过减去历史状态得出某个子树的状态. 所以说,这题妙在思想的转换. 考虑以dfs序为下标,以深度为时间轴建一颗主席树.

主席树入门详解+题目推荐

主席树学名可持久化线段树,就是这个可持久化,衍生了多少数据结构 为什么会有主席树这个数据结构呢?它被发明是用来解决什么问题的呢? 给定n个数,m个操作,操作类型有在某个历史版本下单点修改,输出某个历史版本下某个位置的值的值,n和m小于等于1e6 乍一看是不是一点头绪也没有.我们先来想想暴力怎么做,暴力存储第i个状态下每个数的值,显然这样做不是TLE就是MLE,我们不妨管这种状态叫做TM双LE. 如果没有这个历史状态显然处理很简单,一个线段树就解决了.那么加上历史状态呢?如果我们优化一下暴力,我们

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[]为原