题目描述

墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令: 1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。 2、 R P Col 把第P支画笔替换为颜色Col。为了满足墨墨的要求,你知道你需要干什么了吗?

输入

第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。

输出

对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。

样例输入

6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6

样例输出

4
4
3
4

提示

对于100%的数据,N≤10000,M≤10000,修改操作不多于1000次,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。

 
带修改莫队模板题

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
struct lty
{
int l,r,num,tim;
}q[10010];
struct miku
{
int x,y;
}a[10010];
int res;
int v[1000010];
int s[100010];
int ans[100010];
int num,cnt;
int n,m;
int block;
char ch[20];
bool cmp(lty a,lty b)
{
return (a.l/block==b.l/block)?(a.r==b.r?a.tim<b.tim:a.r<b.r):a.l<b.l;
}
void del(int x)
{
if(v[x]==1)
{
res--;
}
v[x]--;
}
void ins(int x)
{
if(!v[x])
{
res++;
}
v[x]++;
}
void change(int l,int r,int id)
{
if(a[id].x>=l&&a[id].x<=r)
{
if(--v[s[a[id].x]]==0)
{
res--;
}
if(++v[a[id].y]==1)
{
res++;
}
}
swap(a[id].y,s[a[id].x]);
}
int main()
{
scanf("%d%d",&n,&m);
block=pow(n,2/3);
for(int i=1;i<=n;i++)
{
scanf("%d",&s[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%s",ch);
if(ch[0]=='Q')
{
cnt++;
scanf("%d%d",&q[cnt].l,&q[cnt].r);
q[cnt].num=cnt,q[cnt].tim=num;
}
else
{
num++;
scanf("%d%d",&a[num].x,&a[num].y);
}
}
sort(q+1,q+1+cnt,cmp);
int L=1,R=0,now=0;
for(int i=1;i<=cnt;i++)
{
while(L>q[i].l)
{
L--;
ins(s[L]);
}
while(R<q[i].r)
{
R++;
ins(s[R]);
}
while(L<q[i].l)
{
del(s[L]);
L++;
}
while(R>q[i].r)
{
del(s[R]);
R--;
}
while(now<q[i].tim)
{
now++;
change(q[i].l,q[i].r,now);
}
while(now>q[i].tim)
{
change(q[i].l,q[i].r,now);
now--;
}
ans[q[i].num]=res;
}
for(int i=1;i<=cnt;i++)
{
printf("%d\n",ans[i]);
}
}
对于每个点,记录这个点的颜色上一次出现的位置作为这个点的点权,每次查询l,r就相当于查询l到r之间点权小于l的点有多少个。实现起来直接用树套树就好了,外层用线段树维护序列区间信息,内层平衡树维护区间排名,每次只要找线段树对应点的平衡树内l的排名就好了。但修改比较麻烦,每次修改x点需要改变x修改前颜色中x的后继的前驱、x的前驱和修改后颜色中x的后继的前驱。对于每种颜色开一个set来维护前驱后继,每次修改时对应修改就好了,但要注意判断x是否有前驱和后继。
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
int x,y;
int n,m;
int tot;
int opt;
char ch[3];
int a[50010];
int v[2000010];
int w[2000010];
int t[2000010];
int l[500010];
int r[500010];
int ls[2000010];
int rs[2000010];
int pre[50010];
int suf[50010];
int size[2000010];
int root[500010];
map<int,int>b;
set<int>s[1000010];
set<int>::iterator it;
int inbuild(int k)
{
tot++;
t[tot]=rand();
v[tot]=k;
w[tot]=1;
size[tot]=1;
ls[tot]=rs[tot]=0;
return tot;
}
void updata(int rt)
{
size[rt]=size[ls[rt]]+size[rs[rt]]+w[rt];
}
void lturn(int &rt)
{
int t=rs[rt];
rs[rt]=ls[t];
ls[t]=rt;
updata(rt);
updata(t);
rt=t;
}
void rturn(int &rt)
{
int t=ls[rt];
ls[rt]=rs[t];
rs[t]=rt;
updata(rt);
updata(t);
rt=t;
}
void insert(int &rt,int k)
{
if(!rt)
{
rt=inbuild(k);
return ;
}
if(v[rt]==k)
{
w[rt]++;
}
else
{
if(k<v[rt])
{
insert(ls[rt],k);
if(t[ls[rt]]<t[rt])
{
rturn(rt);
}
}
else
{
insert(rs[rt],k);
if(t[rs[rt]]<t[rt])
{
lturn(rt);
}
}
}
updata(rt);
}
void del(int &rt,int k)
{
if(v[rt]<k)
{
del(rs[rt],k);
}
else if(v[rt]>k)
{
del(ls[rt],k);
}
else
{
if(w[rt]>1)
{
w[rt]--;
}
else
{
if(!ls[rt]||!rs[rt])
{
rt=ls[rt]+rs[rt];
}
else
{
if(t[ls[rt]]<t[rs[rt]])
{
rturn(rt);
del(rs[rt],k);
}
else
{
lturn(rt);
del(ls[rt],k);
}
}
}
}
if(rt)
{
updata(rt);
}
}
int inrank(int rt,int k)
{
if(!rt)
{
return 0;
}
if(v[rt]==k)
{
return size[ls[rt]];
}
else if(v[rt]<k)
{
return size[ls[rt]]+w[rt]+inrank(rs[rt],k);
}
else
{
return inrank(ls[rt],k);
}
}
void outbuild(int rt,int L,int R)
{
l[rt]=L;
r[rt]=R;
for(int i=L;i<=R;i++)
{
insert(root[rt],pre[i]);
}
if(L!=R)
{
int mid=(L+R)>>1;
outbuild(rt<<1,L,mid);
outbuild(rt<<1|1,mid+1,R);
}
}
void change(int rt,int x,int y)
{
del(root[rt],pre[x]);
insert(root[rt],y);
if(l[rt]!=r[rt])
{
int mid=(l[rt]+r[rt])>>1;
if(x<=mid)
{
change(rt<<1,x,y);
}
else
{
change(rt<<1|1,x,y);
}
}
}
int outrank(int rt,int L,int R,int k)
{
if(L<=l[rt]&&r[rt]<=R)
{
return inrank(root[rt],k);
}
int mid=(l[rt]+r[rt])>>1;
if(R<=mid)
{
return outrank(rt<<1,L,R,k);
}
else if(L>mid)
{
return outrank(rt<<1|1,L,R,k);
}
return outrank(rt<<1,L,R,k)+outrank(rt<<1|1,L,R,k);
}
int main()
{
srand(12378);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
pre[i]=b[a[i]];
suf[b[a[i]]]=i;
b[a[i]]=i;
s[a[i]].insert(i);
}
for(int i=1;i<=n;i++)
{
if(!suf[i])
{
suf[i]=n+1;
}
}
outbuild(1,1,n);
for(int i=1;i<=m;i++)
{
scanf("%s",ch);
if(ch[0]=='R')
{
scanf("%d%d",&x,&y);
if(a[x]==y)
{
continue;
}
if(pre[x])
{
suf[pre[x]]=suf[x];
}
if(suf[x]!=n+1)
{
change(1,suf[x],pre[x]);
pre[suf[x]]=pre[x];
}
s[a[x]].erase(x);
a[x]=y;
s[a[x]].insert(x);
it=s[a[x]].lower_bound(x);
if(it!=s[a[x]].begin())
{
it--;
suf[(*it)]=x;
change(1,x,(*it));
pre[x]=(*it);
it++;
}
else
{
change(1,x,0);
pre[x]=0;
}
if(++it!=s[a[x]].end())
{
suf[x]=(*it);
change(1,(*it),x);
pre[(*it)]=x;
}
else
{
suf[x]=n+1;
}
}
else
{
scanf("%d%d",&x,&y);
printf("%d\n",outrank(1,x,y,x));
}
}
}

BZOJ2120&2453数颜色——线段树套平衡树(treap)+set/带修改莫队的更多相关文章

  1. 树套树Day1线段树套平衡树bzoj3196

    您需要写一种数据结构,来维护一个有序数列,其中需要提供以下操作:1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询k在区间内的前驱(前驱定义为小于x,且最大的数)5.查 ...

  2. 【BZOJ-3196】二逼平衡树 线段树 + Splay (线段树套平衡树)

    3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 2271  Solved: 935[Submit][Stat ...

  3. BZOJ3196二逼平衡树——线段树套平衡树(treap)

    此为平衡树系列最后一道:二逼平衡树您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名2.查询区间内排名为k的值3.修改某一位值上的数值4.查询 ...

  4. bzoj 3196/ Tyvj 1730 二逼平衡树 (线段树套平衡树)

    3196: Tyvj 1730 二逼平衡树 Time Limit: 10 Sec  Memory Limit: 128 MB[Submit][Status][Discuss] Description ...

  5. bzoj 2120 线段树套平衡树

    先吐下槽,改了快一个小时,最后发现是SBT的delete写错了,顿时就有想死的心..... 首先对于这道题,我们应该先做一下他的小问题,bzoj1878,虽然和这道题几乎一点关系没有, 但是能给我们一 ...

  6. P3380 【模板】二逼平衡树(树套树) 线段树套平衡树

    \(\color{#0066ff}{ 题目描述 }\) 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 查询k在区间内的排名 查询区间内排名为k的值 修改某一位值上 ...

  7. BZOJ3196 二逼平衡树 【线段树套平衡树】

    题目 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: 1.查询k在区间内的排名 2.查询区间内排名为k的值 3.修改某一位值上的数值 4.查询k在区间内的前驱(前驱 ...

  8. 浅谈树套树(线段树套平衡树)&学习笔记

    0XFF 前言 *如果本文有不好的地方,请在下方评论区提出,Qiuly感激不尽! 0X1F 这个东西有啥用? 树套树------线段树套平衡树,可以用于解决待修改区间\(K\)大的问题,当然也可以用 ...

  9. CF 19D - Points 线段树套平衡树

    题目在这: 给出三种操作: 1.增加点(x,y) 2.删除点(x,y) 3.询问在点(x,y)右上方的点,如果有相同,输出最左边的,如果还有相同,输出最低的那个点 分析: 线段树套平衡树. 我们先离散 ...

随机推荐

  1. 解决linux下无线网卡被物理禁用问题

    困扰了我好几天终于解决了这个问题,这里写出来,给再遇到这样问题的朋友做个借鉴! 笔记本:lenovo 问题描述:wifi无线网卡开关是打开的,但是安装linux(fedora \ ubuntu )后, ...

  2. C#的delegate简单练习

    delegate中文的意思为委托. 在很久之前,Insus.NET有写过一篇<用一个简单的例子来演绎事件委托>http://www.cnblogs.com/insus/p/3732075. ...

  3. angularjs自定义指令Directive

    今天学习angularjs自定义指令Directive.Directive是一个非常棒的功能.可以实现我们自义的的功能方法. 下面的例子是演示用户在文本框输入的帐号是否为管理员的帐号"Adm ...

  4. Spring Boot Admin 日志查看功能

    按照官方配置POM和配置文件后,能够结合Eureka查看各微服务状态,但是日志始终查看不了,出现406等错误. 最后偶然发现,是在在从官方网站拷贝配置的时候,出现的问题. logging.file=* ...

  5. 安卓自动化测试案例(跑在MonkeyRunner上)

    首先文件所在目录: MonkeyRunner所在目录: 运行命令(通过cd 命令  进入Tools目录下): 运行脚本:monkeyrunner.bat ..\honeywell\jsq.py 源文件 ...

  6. 大话重构连载15:采用Mock技术完成测试

    第五次重构我们引入了数据库的设计,用户信息要从数据库中读取,问候语库存储在数据库中,并支持添加与更新.数据库的引入使自动化测试变得困难了,因为数据状态总是变化着的,而这种变化使得测试过程不能复现,这是 ...

  7. CrackMe-005全破详解(图文+源码)--上篇

    逆向破解 | 逆向 | 逆向分析 | CrackMe | Crack | CrackMe5 | CrackMe05 前言 CrackMe005,都说比较变态,很多人给放过去了,但是我还是决定上了它,既 ...

  8. Python - 列表解析式

    列表解析——用来动态地创建列表 [expr for iter_var in iterable if cond_expr] 例子一: map(lambda x: x**2, range(6)) [0, ...

  9. Python_函数_复习_习题_24

    # 函数 # 可读性强 复用性强# def 函数名(): # 函数体 #return 返回值# 所有的函数 只定义不调用就一定不执行 #先定义后调用 #函数名() #不接收返回值#返回值 = 函数名( ...

  10. #个人博客作业Week1——浏览教材后提出的六个问题及软件与软件工程的提出。

    1.通常,我们阅读软件比编写软件花费的时间更多.正因为编写软件比阅读软件要容易,因此代码的可读性显得尤为重要.那么我们在写程序时应该如何避免多余的,带有误导性的注释,写出一个利于帮助别人读懂程序的注释 ...