可持久化线段树(主席树)【舰娘系列】【自编题】
2017-07-21 15:30
337 查看
[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%的数据
4000
,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 ,*null,pool[N*40],*tail=pool; struct tt{ int ty,nu; }t ; int n,pre ; 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\n",ans); } return 0; }
总结:
1、遇到区间的询问的题说不定就是主席树
2、如何灵活地运用线段树也是一大技巧,线段树可没有这么简单,高深着呢!
相关文章推荐
- 可持久化线段树(主席树)【舰娘系列】【自编题】
- 【POJ2104】K-th Number-主席树(可持久化线段树)+离散化
- poj-2104 K-th Number[主席树/函数式线段树/可持久化线段树]
- 主席树(可持久化线段树)学习笔记
- 关于 (主席树) (可持久化线段树) (动态开点线段树) 的问题
- 【洛谷3834】 【模板】可持久化线段树 (主席树)
- 【模板】可持久化线段树(主席树)
- [BZOJ2588]Count on a tree(可持久化权值线段树|主席树)
- [ZOJ2112][可持久化线段树(主席树)][树状数组]Dynamic Rankings[好题]
- 主席树(可持久化线段树)
- 可持久化线段树|主席树 POJ 2104 区间第k大的数
- 可持久化线段树/主席树 基础原理和例题
- HDU 2665 Kth number [可持久化线段树 主席树]
- luogu P3834 【模板】可持久化线段树 1(主席树)
- 主席树/可持久化线段树简介(洛谷P3834/P3919)
- 主席树/函数式线段树/可持久化线段树
- luoguP3834 【模板】可持久化线段树 1(主席树)
- 主席树(可持久化线段树) 静态第k大
- hdu 2665 可持久化线段树求区间第K大值(函数式线段树||主席树)
- POJ 2104 kth number 主席树(可持久化线段树)[指针实现]