Description

给定长度为$n$的数组, 定义数字$X$在$[l,r]$内的值为数字$X$在$[l,r]$内最后一次出现位置的下标减去第一次出现位置的下标
给定$m$次询问, 每次询问有三个整数$a,b,c$询问规则如下:
当$a=1$时, 将数组内第$b$个元素更改为$c$
当$a=2$时, 求区间$[b,c]$所有数字的值的和

Input

第一行两个整数$n$,$m$
第二行$n$个整数, 表示数组
第$3$到$3+m$行, 每行三个整数, 表示每次询问。

Output

对于每次$a=2$的询问, 输出一个整数表示答案

Sample Input1

7 6
1 2 3 1 3 2 1
2 3 7
2 1 3
1 7 2
1 3 2
2 1 6
2 5 7

Sample Output1

5
0
7
1

Sample Input2

7 5
1 3 2 1 4 2 3
1 1 4
2 2 3
1 1 7
2 4 5
1 1 7

Sample Output2

0
0

Solution

设初始每个位置对应点$(i,pre[i])$,权值为$i-pre[i]$。可以把初始位置上的点看成矩形单点加操作。
$pre[i]$为$i$这个位置的数上一次出现的位置,若没有则为$0$。
那么查询区间$[L,R]$就相当于查询左下$(L,L)$右上$(R,R)$的矩形的权值和(写写画画可能比较容易明白),可以$CDQ$。
考虑一次修改会影响什么?设$i$位置把$x$修改成$y$,只会影响和$i$相邻的$x$和$y$,这个可以用$set$维护,然后看成若干矩形单点加操作。
那么就可以写一个只有单点加和矩形求和的$CDQ$分治了。

Code

 #include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
#define N (700009)
#define LL long long
using namespace std; struct Que{int x,y,opt,v;}Q[N],tmp[N];
int n,m,cnt,q_num;
int a[N],b[N],pre[N];
LL c[N],ans[N];
set<int>S[N];
set<int>::iterator it; inline int read()
{
int x=,w=; char c=getchar();
while (c<'' || c>'') {if (c=='-') w=-; c=getchar();}
while (c>='' && c<='') x=x*+c-'', c=getchar();
return x*w;
} void Update(int x,int k)
{
for (; x<=n+; x+=(x&-x)) c[x]+=k;
} LL Query(int x)
{
LL ans=;
for (; x; x-=(x&-x)) ans+=c[x];
return ans;
} void CDQ(int l,int r)
{
if (l==r) return;
int mid=(l+r)>>;
CDQ(l,mid); CDQ(mid+,r);
int i=l,j=mid+,k=l-;
while (i<=mid || j<=r)
if (j>r || i<=mid && (Q[i].x<Q[j].x || Q[i].x==Q[j].x && Q[i].opt<Q[j].opt))
{
if (Q[i].opt==) Update(Q[i].y,Q[i].v);
tmp[++k]=Q[i]; ++i;
}
else
{
if (Q[j].opt==)
{
if (Q[j].v>) ans[Q[j].v]+=Query(Q[j].y);
else ans[-Q[j].v]-=Query(Q[j].y);
}
tmp[++k]=Q[j]; ++j;
}
for (int i=l; i<=mid; ++i)
if (Q[i].opt==) Update(Q[i].y,-Q[i].v);
for (int i=l; i<=r; ++i) Q[i]=tmp[i];
}
int main()
{
n=read(); m=read();
for (int i=; i<=n; ++i)
{
a[i]=read(); pre[i]=b[a[i]]; b[a[i]]=i;
S[a[i]].insert(i); Q[++q_num]=(Que){i,pre[i],,i-pre[i]};
}
for (int i=; i<=m; ++i)
{
int opt=read(),x=read(),y=read();
if (opt==)
{
int p1=,n1=;//前驱 后继
it=S[a[x]].find(x);
if (it!=S[a[x]].begin()) --it, p1=*it, ++it;
if ((++it)!=S[a[x]].end()) n1=*it; --it;
S[a[x]].erase(*it); Q[++q_num]=(Que){x,pre[x],,pre[x]-x};
if (n1)
{
Q[++q_num]=(Que){n1,pre[n1],,pre[n1]-n1};
pre[n1]=p1;
Q[++q_num]=(Que){n1,pre[n1],,n1-pre[n1]};
} int p2=,n2=;
a[x]=y; S[a[x]].insert(x);
it=S[a[x]].find(x);
if (it!=S[a[x]].begin()) --it, p2=*it, ++it;
if ((++it)!=S[a[x]].end()) n2=*it; --it;
pre[x]=p2; Q[++q_num]=(Que){x,pre[x],,x-pre[x]};
if (n2)
{
Q[++q_num]=(Que){n2,pre[n2],,pre[n2]-n2};
pre[n2]=x;
Q[++q_num]=(Que){n2,pre[n2],,n2-pre[n2]};
}
}
else
{
++cnt;
Q[++q_num]=(Que){x-,x-,,cnt};
Q[++q_num]=(Que){y,y,,cnt};
Q[++q_num]=(Que){x-,y,,-cnt};
Q[++q_num]=(Que){y,x-,,-cnt};
}
}
for (int i=; i<=q_num; ++i) Q[i].x++, Q[i].y++;
CDQ(,q_num);
for (int i=; i<=cnt; ++i) printf("%lld\n",ans[i]);
}

CF848C:Goodbye Souvenir(CDQ分治)的更多相关文章

  1. Codeforces 848C Goodbye Souvenir [CDQ分治,二维数点]

    洛谷 Codeforces 这题我写了四种做法-- 思路 不管做法怎样,思路都是一样的. 好吧,其实不一样,有细微的差别. 第一种 考虑位置\(x\)对区间\([l,r]\)有\(\pm x\)的贡献 ...

  2. Codeforces 848C Goodbye Souvenir(CDQ 分治)

    题面传送门 考虑记录每个点的前驱 \(pre_x\),显然答案为 \(\sum\limits_{i=l}^{r} i-pre_i (pre_i \geq l)\) 我们建立一个平面直角坐标系,\(x\ ...

  3. Codeforces 848C (cdq分治)

    Codeforces 848C Goodbye Souvenir Problem : 给一个长度为n的序列,有q个询问.一种询问是修改某个位置的数,另一种询问是询问一段区间,对于每一种值出现的最右端点 ...

  4. 【教程】简易CDQ分治教程&学习笔记

    前言 辣鸡蒟蒻__stdcall终于会CDQ分治啦!       CDQ分治是我们处理各类问题的重要武器.它的优势在于可以顶替复杂的高级数据结构,而且常数比较小:缺点在于必须离线操作. CDQ分治的基 ...

  5. BZOJ 2683 简单题 ——CDQ分治

    [题目分析] 感觉CDQ分治和整体二分有着很本质的区别. 为什么还有许多人把他们放在一起,也许是因为代码很像吧. CDQ分治最重要的是加入了时间对答案的影响,x,y,t三个条件. 排序解决了x ,分治 ...

  6. HDU5618 & CDQ分治

    Description: 三维数点 Solution: 第一道cdq分治...感觉还是很显然的虽然题目不能再傻逼了... Code: /*=============================== ...

  7. 初识CDQ分治

    [BZOJ 1176:单点修改,查询子矩阵和]: 1176: [Balkan2007]Mokia Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 200 ...

  8. HDU5322 Hope(DP + CDQ分治 + NTT)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5322 Description Hope is a good thing, which can ...

  9. BZOJ4170 极光(CDQ分治 或 树套树)

    传送门 BZOJ上的题目没有题面-- [样例输入] 3 5 2 4 3 Query 2 2 Modify 1 3 Query 2 2 Modify 1 2 Query 1 1 [样例输出] 2 3 3 ...

随机推荐

  1. JavaWeb学习 (十一)————Session

    一.Session简单介绍 在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下).因此,在需要保存用户数据时,服务 ...

  2. Jenkins入门之界面概览

    安装完Jenkins后,打开浏览器,在浏览器地址栏内输入http://localhost:8080/ 便可以打开jenkins 基于web的管理界面(如果你在安装过程中修改了Jenkins默认端口,则 ...

  3. [转]TFS2010 Team Project Collections

    本文转自:https://www.cnblogs.com/shanyou/archive/2010/04/14/1712252.html Team Foundation Server 2010有一个改 ...

  4. java_单词长度

    题目内容: 你的程序要读入一行文本,其中以空格分隔为若干个单词,以‘.’结束.你要输出这行文本中每个单词的长度.这里的单词与语言无关,可以包括各种符号,比如“it's”算一个单词,长度为4.注意,行中 ...

  5. 本地存储之sessionStorage

    源码可以到GitHub上下载! sessionStorage: 关闭浏览器再打开将不保存数据   复制标签页会连同sessionStorage数据一同复制 复制链接地址打开网页不会复制seession ...

  6. js识别设备

    console.log(window.navigator); Navigator 对象属性         appCodeName     返回浏览器的代码名.         appMinorVer ...

  7. loadrunner 运行场景-运行时设置

    运行场景-运行时设置 by:授客 QQ:1033553122 A.   查看.修改单个脚本的运行时设置 a)   途径1: Scenario Groups.Scenario Groups Script ...

  8. Java并发编程(六)volatile关键字解析

    由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识. 一.内存模型的相关概念 Java内存模型规定所有的变量都是存在 ...

  9. OneAPM大讲堂 | Metrics, Tracing 和 Logging 的关系

    [编者按]这是在 OpenTracing 和分布式追踪领域内广受欢迎的一片博客文章.在构建监控系统时,大家往往在这几个名词和方式之间纠结. 通过这篇文章,作者很好的阐述了分布式追踪.统计指标与日志之间 ...

  10. [20180408]那些函数索引适合字段的查询.txt

    [20180408]那些函数索引适合字段的查询.txt --//一般不主张建立函数索引,往往是开发的无知,使用trunc等函数,实际上一些函数也可以用于字段的查询.--//以前零碎的写过一些,放假看了 ...