主席树初学 SPOJ3267

别的没管,直接上的kuangbin代码,懂是基本懂了,然而主席树博大精深们还要多多学习。

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <time.h>
using namespace std;

const int MAXN = 30010;
const int M = MAXN * 100;
int n,q,tot;
int a[MAXN];
int T[M],lson[M],rson[M],c[M];
int build(int l,int r)
{

    int root = tot++;
//    printf ("%d---%d,%d\n",l,r,root);
    c[root] = 0;
    if(l != r)
    {
        int mid = (l+r)>>1;
        lson[root] = build(l,mid);
        rson[root] = build(mid+1,r);
    }
    return root;
}
int update(int root,int pos,int val)
{
    int newroot = tot++;//全新的节点
    int tmp = newroot;//记录,说明这个点开始是新的树,即将开始找
    c[newroot] = c[root] + val;//这个点的值是上一个节点再加上这个新的值(因为这个值保证全新)
    int l = 1, r = n;//总区间
    while(l < r)//说明还没到最深
    {
        int mid = (l+r)>>1;//左右分界
        if(pos <= mid)//在区间左边,说明右边不用动,直接拿来用,指一下
        {
            lson[newroot] = tot++;//左边新开节点记录
            rson[newroot] = rson[root];//右边承接上一个
            newroot = lson[newroot];//去新的点
            root = lson[root];//上一个根跟着深入左边
            r = mid;//右边不用管了(确定层数)
        }
        else//左边是还没有更新的节点,但是仍然要指,不然会漏,主要用于删除
        {
            rson[newroot] = tot++;
            lson[newroot] = lson[root];
            newroot = rson[newroot];
            root = rson[root];
            l = mid+1;
        }
        c[newroot] = c[root] + val;//新的点都要加(因为深入的地方总是带着这个新点)
    }
    return tmp;//返回节点
}
int query(int root,int pos)
{
    int ret = 0;
    int l = 1, r = n;
    while(pos < r)
    {
        int mid = (l+r)>>1;
        if(pos <= mid)//在左边,说明这个点不足,只有深入
        {
            r = mid;//右边舍去
            root = lson[root];//根去左边
        }
        else
        {
            ret += c[lson[root]];//在右边,说明左边的这个子区间可以全加上
            root = rson[root];
            l = mid+1;
        }
    }
    return ret + c[root];//最后一个左区间
}

int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    while(scanf("%d",&n) == 1)
    {
        tot = 0;
        for(int i = 1; i <= n; i++)
            scanf("%d",&a[i]);
        T[n+1] = build(1,n);
        map<int,int>mp;
        for(int i = n; i>= 1; i--) //T[i]中是对每一个数(位置)的根
        {
            if(mp.find(a[i]) == mp.end())
            {
                T[i] = update(T[i+1],i,1);
//                printf ("1---%d---%d\n",a[i],T[i+1]);
            }
            else
            {
                int tmp = update(T[i+1],mp[a[i]],-1);//为了清除之前的状态,但是不得记录,就当是瞎了
                T[i] = update(tmp,i,1);
            }
            mp[a[i]] = i;
        }
        scanf("%d",&q);
        while(q--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            printf("%d\n",query(T[l],r));//在l这棵树中找r
        }
    }
    return 0;
}
时间: 2024-12-08 09:18:12

主席树初学 SPOJ3267的相关文章

spoj3267 D-query 主席树(可持久化线段树)

题目链接 题意:给n个数,m次查询,求[l,r]之间不重复数的个数. 思路:主席树.用一个map记录每个值在当前操作下最新的位置,从前往后插入主席树.对于查询[l,r],窝们在root[ l ]下查询在r之前的不重复数的个数.详见代码: /********************************************************* file name: spoj3267.cpp author : kereo create time: 2015年04月04日 星期六 14时2

SPOJ3267 D-query(主席树模版)

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

SPOJ 3267. D-query (主席树)

A - D-query Time Limit:1500MS     Memory Limit:0KB     64bit IO Format:%lld & %llu Given a sequence of n numbers a1, a2, ..., an and a number of d-queries. A d-query is a pair (i, j) (1 ≤ i ≤ j ≤ n). For each d-query (i, j), you have to return the nu

POJ_2104_Kth(主席树)

描述 http://poj.org/problem?id=2104 给出一个n个数的数列,m次询问,每次询问求区间[l,r]中第k小的数,无修改操作. 分析 静态的主席树裸题. 首先考虑把数据离散化,这样一共有n个数,分别为1,2,...,n-1,n(如果没有重复的话)(如果题目里面说有重复且重复数字排名相同,就去一下重就好了).用N=n的线段树来表示某一区间当前的情况,其中节点a[k]表示在这个区间内,属于[a[k].L,a[k].R]的数字共有多少.这样在这个区间上求第K小数的操作就类似于平

主席树的各类模板(区间第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

[poj2104]可持久化线段树入门题(主席树)

解题关键:离线求区间第k小,主席树的经典裸题: 对主席树的理解:主席树维护的是一段序列中某个数字出现的次数,所以需要预先离散化,最好使用vector的erase和unique函数,很方便:如果求整段序列的第k小,我们会想到离散化二分和线段树的做法, 而主席树只是保存了序列的前缀和,排序之后,对序列的前缀分别做线段树,具有差分的性质,因此可以求任意区间的第k小,如果主席树维护索引,只需要求出某个数字在主席树中的位置,即为sort之后v中的索引:若要求第k大,建树时反向排序即可 1 #include

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

BZOJ_3207_花神的嘲讽计划1_(Hash+主席树)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=3207 给出一个长度为\(n\)的串,以及\(m\)个长度为\(k\)的串,求每个长度为\(k\)的串在原串\([x,y]\)区间是否出现过. 分析 这道题要求对比长度为\(k\)的串,于是我们把这些串的Hash值都算出来,问题就转化成了求\([x,y]\)的区间中是否出现过某Hash值. 求区间中某一个值出现了多少次,可以用主席树. p.s. 1.学习了主席树指针的写法,比数组慢好多啊...