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

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

这里写图片描述

[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
#include
#include
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.tyreturn a.nu*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(midrs,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、如何灵活地运用线段树也是一大技巧,线段树可没有这么简单,高深着呢!

转载于:https://www.cnblogs.com/LinnBlanc/p/7763153.html

更多相关:

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...

  • 在EWM中很少有创建或者修改业务对象的BAPI存在,更多的是通过很多面向对象的类方法来实现。 以下这个简单的创建TU应该能很好的体现SCM平台中的OO特性。 REPORT yewm_tu_creation NO STANDARD PAGE HEADING. TYPES:   BEGIN OF lty_key_wrk,     tu_n...

  • 001.删除不以特定字符串结尾的文件 1.) rm -f `ls | grep -v '.c$'`(删除所有的非c源文件: ls前面" ' "为反向引号, 键盘1前面的键; "." 需要转义) 转载于:https://www.cnblogs.com/itpoorman/p/3858611.html...