CF848C Goodbye Souvenir
前言
题解清一色的
这是一个分块做法,复杂度
如果你真心想写这个做法,建议写完这题就去写
转化
考虑记下每个位置前一个与它颜色相同的位置
那么我们可以把这个贡献从每种颜色转化到每个位置。
方法是定义每个位置的贡献为
假如一个颜色所有出现的位置为
可以看出最后就是我们想求的。
转化完后,我们把这个问题换成一个二维数点的形式,即有
关于点的性质:所有点的
修改操作
很平凡。你维护每个点出现位置的 set,然后就可以
二维分块
这个题用这玩意确实大材小用了,但是还是想说说。
注意:下面介绍的是一种
举个例子,假设我们要加的是这么一个矩形(左下角是原点,右上角是询问点)。
为了方便,以下记
首先因为整块复杂度要保证,所以块数是
但是我们整块复杂度保证,散块又不行了。于是考虑在现在的灰色部分分块用来辅助原来的散块,分出
现在总共分了
现在来算一下复杂度。
- 对于红色块而言,它一定不超过
\sqrt n 个。 - 对于橙色块而言,它一行最多只有
n\div\mathbf c=n^{0.25} 个,一列最多只有\mathbf{c\div b}=n^{0.25} 个,乘起来不超过\sqrt n 个。 - 对于蓝色块而言,它一行最多只有
\mathbf{c\div b}=n^{0.25} 个,一列最多只有n\div\mathbf c=n^{0.25} 个,乘起来不超过\sqrt n 个。 - 对于绿色块而言,它一行/一列最多只有
\mathbf{c\div b}=n^{0.25} 个,乘起来也不超过\sqrt n 个。 - 散块需要结合这题的特殊性质才可做,这里不作讨论,下面讲。
那散块如何做呢?
首先,散块本身的定义是:一个询问覆盖了一个点但没有完全覆盖这个点所在的
上文所说的性质,就是说
基于性质,我们把散块拆分为三部分。
对于紫色部分,枚举紫色范围内
对于粉色部分,枚举粉色范围内
你可以在紫色部分的时候算上蓝色部分,也可以在粉色部分的时候算上蓝色部分,不要算重就行了。
Code
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<set>
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define Rep(i,a,b) for(int i = (a);i >= (b);--i)
#define clr(M) memset(M,0,sizeof M)
const int N = 1e5 + 5;
typedef long long ll;
int n,m,a[N],p[N],q[N];
namespace blk2d{
const int B1 = 18,B2 = 324,B3 = 5832;
const int S1 = 25,S2 = 330,S3 = 5840;
int st2[S2],bl2[N],st3[S1],bl3[N]; ll c0[N],s0[S2];
ll c1[S1][S1],c2[S2][S1],c3[S1][S2],c4[S2][S2],c5[N],c6[N];
#define F1(i,z) rep(i,1,bl3[z] - 1)
#define F2(i,z) rep(i,st3[bl3[z]],bl2[z] - 1)
#define F3(i,z) rep(i,st2[bl2[z]],z)
#define bl(i,t) (i - 1) / t + 1
void init(){
rep(i,1,n) bl2[i] = bl(i,B2),bl3[i] = bl(i,B3);
Rep(i,n,1) st2[bl2[i]] = i,st3[bl3[i]] = bl2[i];
}
void upd(int x,int y,ll t){
p[x] = y; q[y] = x;
if(y == 0) return c0[x] += t,void(s0[bl2[x]] += t);
int a = bl3[x],b = bl2[x],c = bl3[y],d = bl2[y];
c1[a][c] += t; c2[b][c] += t; c3[a][d] += t; c4[b][d] += t;
c5[x] += t; c6[y] += t;
}
ll qry(int x,int y){
ll sum = 0;
F1(i,x) F1(j,y) sum += c1[i][j];
F2(i,x) F1(j,y) sum += c2[i][j];
F1(i,x) F2(j,y) sum += c3[i][j];
F2(i,x) F2(j,y) sum += c4[i][j];
F3(i,x) if(p[i] <= y) sum += c5[i];
F3(i,y) if(q[i] < st2[bl2[x]]) sum += c6[i];
rep(i,1,bl2[x] - 1) sum += s0[i];
rep(i,st2[bl2[x]],x) sum += c0[i];
return sum;
}
}
using namespace blk2d;
std::set<int> P[N];
int pre(int x){
auto i = P[a[x]].lower_bound(x);
return i == begin(P[a[x]]) ? 0 : *--i;
}
int nxt(int x){
auto i = P[a[x]].upper_bound(x);
return i == end(P[a[x]]) ? 0 : *i;
}
void cpre(int u){
if(!u) return;
int v = pre(u);
upd(u,p[u],p[u] - u);
upd(u,v,u - v);
}
int main(){
scanf("%d%d",&n,&m);
rep(i,1,n) scanf("%d",a + i),P[a[i]].insert(i);
init();
rep(u,1,n) p[u] = pre(u),upd(u,p[u],u - p[u]);
while(m--){
int op,x,y;
scanf("%d%d%d",&op,&x,&y);
if(op == 1){
int w = nxt(x);
P[a[x]].erase(x); P[a[x] = y].insert(x);
cpre(x); cpre(w); int v = nxt(x); cpre(v);
q[p[x]] = x; q[p[w]] = w; q[p[v]] = v;
} else printf("%lld\n",qry(y,n) - qry(y,x - 1) - qry(x - 1,n) + qry(x - 1,x - 1));
}
return 0;
}