• 可持久化线段树(主席树)【舰娘系列】【自编题】


    这里写图片描述
    [pixiv] https://www.pixiv.net/member_illust.php?mode=medium&illust_id=60083619
    向大(hei)佬(e)势力学(di)习(tou)

    前段时间做了一套大佬自己出的题(大佬竟然是个宅男2333),蒟蒻的我自然是只得了30分的暴力分:-(

    fleet

    舰队

    【题目描述】
    舰队里的每位舰娘都有一个编号i,也有一个类型ti,例如驱逐舰、轻巡洋
    舰、航空母舰……
    每次提督都想知道从第l 位舰娘到第r 位舰娘中(包含l、r),一共有多
    少不同的类型。请你回答他的询问。
    【输入格式】
    第一行一个整数n,表示舰娘数量。
    第二行n 个整数,第i 个整数ti 表示舰娘i 的类型。
    第三行一个整数q,表示提督的询问数量。
    接下来q 行,每行2 个整数l,r,表示询问[l,r]有多少不同类型的舰娘。
    为了模拟真实情况,本题强制在线。你需要记录上一次的答案lastans(初
    始值为0),每次询问真实的l,r 为l xor lastans 与r xor lastans,其中xor
    表示按位异或操作。
    【输出格式】
    输出q 行,每行一个整数表示询问的答案。
    【样例数据】
    fleet.in
    5
    1 1 2 1 3
    3
    1 5
    1 7
    1 7
    fleet.out
    3
    2
    3
    【数据范围】
    对于30%的数据,n,q≤5000。
    对于另30%的数据,ti≤100000。
    对于100%的数据,1≤n,q≤200000,1≤l≤r≤n,1≤ti≤10^9。

    正解当然不是我想出来的,而是另一个大佬自己yy出来的

    我们建的线段树表示原序列中出现的不同的数的个数,就是对原序列建树,只不过对于节点i的线段树表示1~i区间出现的不同数的个数。
    建树的时候需要记录这一个数上一次出现的位置,新建一棵树的时候既要加,又要减。
    查询的时候利用前缀和区间查询即可
    注意要离散化
    然后就没更多的技巧了,但是将这一模型抽出来着实很厉害

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int N=200000+5;
    
    struct Node {
        Node *ls,*rs;
        int sum;
        void update(){
            sum=ls->sum+rs->sum;
        }
    }*root[N],*null,pool[N*40],*tail=pool;
    struct tt{
        int ty,nu;
    }t[N];
    int n,pre[N];
    
    bool cmp1(tt a,tt b){
        return a.ty<b.ty;
    }
    bool cmp2(tt a,tt b){
        return a.nu<b.nu;
    }
    Node *newnode(){
        Node *nd=++tail;
        nd->ls=nd->rs=null;
        nd->sum=0;
        return nd;
    }
    Node *build(int le,int ri){
        Node *nd=newnode();
        if(le==ri) return nd;
        int mid=(le+ri)>>1;
        nd->ls=build(le,mid);
        nd->rs=build(mid+1,ri);
    }
    void insert(Node *&ndn,Node *ndp,int le,int ri,int pos,int val){
        ndn=newnode();
        ndn->sum=ndp->sum+val;
        ndn->ls=ndp->ls,ndn->rs=ndp->rs;
        if(le==ri) return ;
        int mid=(le+ri)>>1;
        if(pos<=mid) insert(ndn->ls,ndp->ls,le,mid,pos,val);
        else insert(ndn->rs,ndp->rs,mid+1,ri,pos,val);
    }
    int query(Node *nd,int le,int ri,int L,int R){
        if(L<=le&&ri<=R){//printf("eh ");
            return nd->sum;
        }
        int mid=(le+ri)>>1;
        int rt=0;
        if(L<=mid) rt+=query(nd->ls,le,mid,L,R);
        if(mid<R) rt+=query(nd->rs,mid+1,ri,L,R);
        return rt;
    }
    int main(){
        freopen("fleet.in","r",stdin);
        freopen("fleet.out","w",stdout);
        null=++tail;
        null->ls=null->rs=null;
        null->sum=0;
    
        scanf("%d",&n);
        root[0]=build(1,n);
        for(int i=1;i<=n;i++){
            scanf("%d",&t[i].ty);
            t[i].nu=i;
        }
        sort(t+1,t+n+1,cmp1);
        int sz=0,tmp=0;
        for(int i=1;i<=n;i++){
            if(tmp!=t[i].ty){
                tmp=t[i].ty;sz++;t[i].ty=sz;
            }
            else t[i].ty=sz;
        }
        sort(t+1,t+n+1,cmp2);
        tmp=0;
        for(int i=1;i<=n;i++){
            insert(root[i],root[i-1],1,n,i,1);
            if(pre[t[i].ty]){//printf("he ");
                tmp=pre[t[i].ty];
                insert(root[i],root[i],1,n,tmp,-1);
            } 
            pre[t[i].ty]=i;
        }
        int q,l,r,ans=0;
        scanf("%d",&q);
        while(q--){
            scanf("%d%d",&l,&r);
            l^=ans;r^=ans;
            ans=query(root[r],1,n,l,r);
            printf("%d
    ",ans);
        }
        return 0;
    }

    总结:
    1、遇到区间的询问的题说不定就是主席树
    2、如何灵活地运用线段树也是一大技巧,线段树可没有这么简单,高深着呢!

  • 相关阅读:
    可变性编程 不可变性编程 可变性变量 不可变性变量 并发编程 命令式编程 函数式编程
    hashable
    优先采用面向表达式编程
    内存转储文件 Memory.dmp
    windows update 文件 路径
    tmp
    查询局域网内全部电脑IP和mac地址等信息
    iptraf 网卡 ip 端口 监控 netstat 关闭端口方法
    Error 99 connecting to 192.168.3.212:6379. Cannot assign requested address
    t
  • 原文地址:https://www.cnblogs.com/LinnBlanc/p/7763153.html
Copyright © 2020-2023  润新知