又是一道比较新的模板题吧,即使是在Codeforces上小C还是贴了出来。

Description

  给定一个长度为n的序列a1~an,每个元素代表一种颜色。m次操作,每次操作为两种中的一种:

    1 p x:将第p个位置上的颜色修改为x;

    2 l r:询问[l,r]区间,求该区间内的每种颜色的“最大出现位置-最小出现位置”之和。

Input

  第一行两个正整数n、m;

  第二行n个整数,表示a1~an;

  接下来m行,每行表示一个如题所示的操作。

Output

  对于每个操作2,输出题目所求的答案。

Sample Input

  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 Output

  5
  0
  7
  1

HINT

  1 ≤ n,m ≤ 100 000,1≤ ai ≤ n;

  1 ≤ p,x ≤ n,1 ≤ l ≤ r ≤ n。

Solution

  应该说入手这道题还是很容易的,不管后面是怎么做,我们首先可以判定它是一道数据结构题。

  我们考虑对于每个元素,我们维护上一个出现它的颜色的位置。

  这样似乎就成为了我们很熟悉的矩形询问一类的问题。我们类比一下询问区间的颜色种数怎么做:

  第一维代表区间下标,第二维代表上一次出现该颜色的位置,要维护的信息是该位置出现的次数(其实只有0和1),目的是求和。

  同理这一题似乎同样可以这么做:

  第一维代表区间下标,第二维代表上一次出现该颜色的位置,要维护的信息是 区间下标与上一次出现的位置的差 ,目的是求和。

  这样似乎就很完美,我们可以直接树套树……然后并不能很爽地通过该题,因为炸空间了。

  那这可咋办呀,我们就可以用到我们神奇的分治算法——cdq分治!

  cdq算法的主要思想就是将操作区间分成两半,计算前一半操作对后一半询问的影响。

  这样就相当于将在线的修改去掉,将询问改为离线。

  这也就要求询问具有可合并性,如果操作之间会互相影响,cdq就不管用了。

  例如操作是加法而询问是取max,这样的询问是不满足可合并性的。

  对于这道题,每个操作对于答案的影响是独立的,且每次修改颜色都会改变至多6次我们所维护的信息:

  设pre[x]为上一次出现该color[x]的位置,suc[x]为下一次出现color[x]的位置。而我们只要维护pre[x]。

  假设修改pre[x],改之后的pre[x]为npre[x],suc[x]同理。

  要修改的所有信息为:pre[x],pre[suc[x]],pre[nsuc[x]]。

  对于每个信息在二维平面上的操作是一次单点减和一次单点加,所以总共是3*2=6次。

  完全转化成离线操作后,就只有询问矩形和了,把询问排序用一个普通线段树都是可以做的。

  每次操作出现在logm个分治区间里,单个操作的复杂度是logn,所以总时间复杂度O(mlogmlogn)。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
#define ll long long
#define MM 264005
#define MN 500005
using namespace std;
struct meg{int ki,pos,lf,rf,val,aps;}px[MN];
struct node{int g,x,y;}b[MM];
set <int> se[MM];
ll t[MM],ans[MM];
int c[MM][],las[MM],pre[MM],col[MM];
int MQ,n,m,pxin; inline int read()
{
int n=,f=; char c=getchar();
while (c<'' || c>'') {if(c=='-')f=-; c=getchar();}
while (c>='' && c<='') {n=n*+c-''; c=getchar();}
return n*f;
} inline void getadd(int x,int z) {for (x+=MQ;x;x>>=) t[x]+=z;}
inline ll getsum(int x,int y)
{
register ll lt=;
for (x+=MQ,y+=MQ;x<=y;x>>=,y>>=)
{
if ( x&) lt+=t[x++];
if (~y&) lt+=t[y--];
}
return lt;
} bool cmp(const meg& a,const meg& b) {return a.pos<b.pos || a.pos==b.pos && a.ki<b.ki;}
void solve()
{
sort(px+,px+pxin+,cmp);
register int i;
for (i=;i<=pxin;++i)
if (px[i].ki==) {if (px[i].lf) getadd(px[i].lf,px[i].val);}
else ans[px[i].aps]+=getsum(px[i].lf,px[i].rf)*px[i].val;
for (i=;i<=pxin;++i) if (px[i].ki==&&px[i].lf) getadd(px[i].lf,-px[i].val);
} void work(int L,int R,int gs)
{
if (!gs||L==R) return;
int i,qet=,mid=L+R>>;
pxin=;
for (i=L;i<=mid;++i)
if (b[i].g==)
{
px[++pxin]=(meg){,b[i].x,c[i][],,c[i][]-b[i].x,};
px[++pxin]=(meg){,b[i].x,c[i][],,b[i].x-c[i][],};
px[++pxin]=(meg){,c[i][],b[i].x,,b[i].x-c[i][],};
px[++pxin]=(meg){,c[i][],c[i][],,c[i][]-c[i][],};
px[++pxin]=(meg){,c[i][],c[i][],,c[i][]-c[i][],};
px[++pxin]=(meg){,c[i][],b[i].x,,c[i][]-b[i].x,};
}
for (i=mid+;i<=R;++i)
if (b[i].g==)
{
px[++pxin]=(meg){,b[i].x-,b[i].x,b[i].y,-,i};
px[++pxin]=(meg){,b[i].y ,b[i].x,b[i].y, ,i};
++qet;
}
solve(); work(L,mid,gs-qet); work(mid+,R,qet);
} int main()
{
register int i,x,qet=;
n=read(); m=read();
pxin=;
for (MQ=;MQ<n;MQ<<=); --MQ;
for (i=;i<=n;++i)
{
col[i]=x=read();
las[i]=pre[x]; pre[x]=i;
se[x].insert(i);
px[++pxin]=(meg){,i,las[i],,i-las[i],};
}
for (i=;i<=m;++i)
{
b[i].g=read(); b[i].x=read(); b[i].y=read();
if (b[i].g==)
{
set<int> ::iterator k;
k=se[col[b[i].x]].lower_bound(b[i].x);
if (k!=se[col[b[i].x]].begin()) --k,c[i][]=*k,++k; else c[i][]=;
if ((++k)!=se[col[b[i].x]].end()) c[i][]=*k; else c[i][]=;
se[col[b[i].x]].erase(--k);
col[b[i].x]=b[i].y;
k=se[col[b[i].x]].lower_bound(b[i].x);
if (k!=se[col[b[i].x]].end()) c[i][]=*k; else c[i][]=;
if (k!=se[col[b[i].x]].begin()) c[i][]=*(--k); else c[i][]=;
se[col[b[i].x]].insert(b[i].x);
}
else
{
px[++pxin]=(meg){,b[i].x-,b[i].x,b[i].y,-,i};
px[++pxin]=(meg){,b[i].y ,b[i].x,b[i].y, ,i};
++qet;
}
}
solve(); work(,m,qet);
for (i=;i<=m;++i) if (b[i].g==) printf("%I64d\n",ans[i]);
}

Last Word

  感觉这题会让人觉得恶心的只有set的插入删除操作了。

  相比树套树,只需要用到普通线段树还是比较赏心悦目的。

[Codeforces]849E Goodbye Souvenir的更多相关文章

  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 - Goodbye Souvenir

    题目大意:n个数字,m次操作,支持修改一个数字和查询一个区间内每种数字最大出现位置减最小出现位置的和.(n,m<=100,000) 做法:把每个数字表示成二维平面上的点,第一维是在数组中的位置, ...

  4. 【Codeforces 848C】Goodbye Souvenir

    Codeforces 848 C 题意:给\(n\)个数,\(m\)个询问,每一个询问有以下类型: 1 p x:将第p位改成x. 2 l r:求出\([l,r]\)区间中每一个出现的数的最后一次出现位 ...

  5. CF848C:Goodbye Souvenir(CDQ分治)

    Description 给定长度为$n$的数组, 定义数字$X$在$[l,r]$内的值为数字$X$在$[l,r]$内最后一次出现位置的下标减去第一次出现位置的下标给定$m$次询问, 每次询问有三个整数 ...

  6. Codeforces 848C (cdq分治)

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

  7. 【Codeforces Round 431 (Div. 2) A B C D E五个题】

    先给出比赛地址啦,感觉这场比赛思维考察非常灵活而美妙. A. Odds and Ends ·述大意:      输入n(n<=100)表示长度为n的序列,接下来输入这个序列.询问是否可以将序列划 ...

  8. Codeforces Goodbye 2018

    Goodbye 2018 可能是我太菜考试的时候出不了$E$ 可能是我太菜考试的时候调不出$F$ 所以转化为手速场之后手速还上不去.jpg A 模拟题意... #include <cstdio& ...

  9. CodeForces Goodbye 2017

    传送门 A - New Year and Counting Cards •题意 有n张牌,正面有字母,反面有数字 其中元音字母$a,e,o,i,u$的另一面必须对应$0,2,4,6,8$的偶数 其他字 ...

随机推荐

  1. 项目Beta冲刺Day7

    项目进展 李明皇 今天解决的进度 部分数据传递和使用逻辑测试 林翔 今天解决的进度 服务器端查看个人发布的action,修改已发布消息状态的action,仍在尝试使用第三方云存储功能保存图片 孙敏铭 ...

  2. python 操作Memcached

    启动Memcached memcached -d -m 10 -u root -l 10.211.55.4 -p 12000 -c 256 -P /tmp/memcached.pid 参数说明: -d ...

  3. Java语言基础组成

    写完才发现,这个博客不提供目录这个功能,真是想骂爹了...... 目录 关键字 标识符 注释 常量和变量 运算符 语句 函数 数组 1.关键字 描述:刚刚开始学这个的时候,真是傻傻分不清楚,不过没关系 ...

  4. Python内置函数(11)——complex

    英文文档: class complex([real[, imag]]) Return a complex number with the value real + imag*1j or convert ...

  5. MySQL中使用sql语句获得表结构

    最近在研究PHP,那么就必须涉及到mysql.其中一个功能通过表数据自动生成页面,紧接着发现在一张空表中无法读取数据(因为人家刚刚新建,就是空的没有数据) 延伸出来便是直接查表结构获得字段名,再进行处 ...

  6. unity3D 知识点随手记

    最近闲来无事,记记unity3D相关的一些知识点吧,也当作笔记存储.转载请标明出处:http://www.cnblogs.com/zblade/ 1.unity是如何调用Start/Awake等相关函 ...

  7. Couchbase忘记登录密码怎么办

    以下都为root用户操作: 1.先关闭couchbase /opt/couchbase/etc/couchbase_init.d stop 2.切换到下面的路径,删除这个目录下除logs的所有文件,按 ...

  8. Javascript 判断传入的两个数组是否相似

    任务描述: 请在index.html文件中,编写arraysSimilar函数,实现判断传入的两个数组是否相似.具体需求: 1. 数组中的成员类型相同,顺序可以不同.例如[1, true] 与 [fa ...

  9. JavaScript中的 原型 property 构造函数 和实例对象之间的关系

    1 为什么要使用原型? /* * javascript当中 原型 prototype 对象 * * */ //首先引入 prototype的意义,为什么要使用这个对象 //先来写一个构造函数的面向对象 ...

  10. python request

    python request a. 客户端向服务端发送多层字典的值 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 obj =  ...