CF848C:Goodbye Souvenir(CDQ分治)
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分治)的更多相关文章
- Codeforces 848C Goodbye Souvenir [CDQ分治,二维数点]
洛谷 Codeforces 这题我写了四种做法-- 思路 不管做法怎样,思路都是一样的. 好吧,其实不一样,有细微的差别. 第一种 考虑位置\(x\)对区间\([l,r]\)有\(\pm x\)的贡献 ...
- Codeforces 848C Goodbye Souvenir(CDQ 分治)
题面传送门 考虑记录每个点的前驱 \(pre_x\),显然答案为 \(\sum\limits_{i=l}^{r} i-pre_i (pre_i \geq l)\) 我们建立一个平面直角坐标系,\(x\ ...
- Codeforces 848C (cdq分治)
Codeforces 848C Goodbye Souvenir Problem : 给一个长度为n的序列,有q个询问.一种询问是修改某个位置的数,另一种询问是询问一段区间,对于每一种值出现的最右端点 ...
- 【教程】简易CDQ分治教程&学习笔记
前言 辣鸡蒟蒻__stdcall终于会CDQ分治啦! CDQ分治是我们处理各类问题的重要武器.它的优势在于可以顶替复杂的高级数据结构,而且常数比较小:缺点在于必须离线操作. CDQ分治的基 ...
- BZOJ 2683 简单题 ——CDQ分治
[题目分析] 感觉CDQ分治和整体二分有着很本质的区别. 为什么还有许多人把他们放在一起,也许是因为代码很像吧. CDQ分治最重要的是加入了时间对答案的影响,x,y,t三个条件. 排序解决了x ,分治 ...
- HDU5618 & CDQ分治
Description: 三维数点 Solution: 第一道cdq分治...感觉还是很显然的虽然题目不能再傻逼了... Code: /*=============================== ...
- 初识CDQ分治
[BZOJ 1176:单点修改,查询子矩阵和]: 1176: [Balkan2007]Mokia Time Limit: 30 Sec Memory Limit: 162 MBSubmit: 200 ...
- HDU5322 Hope(DP + CDQ分治 + NTT)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5322 Description Hope is a good thing, which can ...
- 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 ...
随机推荐
- SpringBoot学习(一)—— web项目基础搭建
首先我们在浏览器打开这个网站 https://start.spring.io/ 打开后可以看到以下页面 在这里我们可以快速搭建一个SpringBoot基础项目,填写和选择完相应的信息后,我们点击那个绿 ...
- Jmeter获取接口返回数组的长度
添加BeanShell PostProcessor来获取,具体代码如下: import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath ...
- Jquery闪烁提示特效
样式:.red{ border:1px solid #d00; background:#ffe9e8; color:#d00;} function shake(ele,cls,times){ var ...
- 【多线程】Task
介绍 Task是.NET推出数据任务处理的工作类.位于System.Threading.Tasks命名空间下,通过命名空间也可以看出是个多线程类. 创建Task: Task有很多构造函数,无参有参都有 ...
- Java字符串String
Java字符串String 我们知道Java的字符窜是Immutable(不可变)的,一旦创建就不能更改其内容了:平常我们对字符串的操作是最多的,其实对字符串的操作,返回的字符串都是新建的字符串对象, ...
- Java学习笔记之——Java介绍
1.Java体系: JavaSE:标准版,其他两个体系的基础 JavaEE:企业版 JavaME:微型版,适用于消费类型的微型设备 2.Java三大特性:封装.继承.多态 3.Java的特点:面向对象 ...
- AOP 怎么理解?
什么是 AOP ? AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP 是 OOP ...
- NIO学习笔记四 :SocketChannel 和 ServerSocketChannel
Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道.可以通过以下2种方式创建SocketChannel: 打开一个SocketChannel并连接到互联网上的某台服务器. ...
- C语言字符串读入函数笔记
gets(str)函数和scanf("%s",str)区别: 转自:https://zhidao.baidu.com/question/290403568.html 二者都是从终端 ...
- JqGrid: paging int asp.net
https://www.codeproject.com/Articles/1118363/GridView-with-Server-Side-Filtering-Sorting-and-Pa http ...