今天,接触信息学不久的小 A
刚刚学习了莫队。
莫队可以解决一类难以合并,但方便插入的信息维护。比如,给定一个序列,支持单点修改,每次询问一个区间出现了多少种数字。再比如,给定一个序列,支持单点修改,每次询问区间众数。诸如此类。
小 A
觉得这样的情况太平凡了。于是,他定义了一个区间是无重的,当且仅当区间内没有重复的数字。具体的,一个区间
[
l
,
r
]
[l,r]
[l,r] 无重,当且仅当,
∀
l
≤
i
<
j
≤
r
\forall l\le i< j \le r
∀l≤i<j≤r,都有
a
i
≠
a
j
a_i\not=a_j
ai=aj。
他给定了一个长度为 n n n 序列 a a a,并给出 m m m 组操作:每次修改一个位置的数,或者询问一个区间 [ l , r ] [l,r] [l,r] 中,有多少个无重的子区间。
输入第一行为两个正整数 n 、 m n、m n、m,分别表示序列的长度,以及操作总数。
第二行有 n n n 个整数,第 i i i 个数表示 a i a_i ai。
接下来共有 m m m 行,每行有三个整数, o p t , x , y opt,x,y opt,x,y。
- 如果 o p t = 1 opt=1 opt=1,表示将 a x a_x ax 修改为 y y y;
- 如果
o
p
t
=
2
opt=2
opt=2,表示询问
[
x
,
y
]
[x,y]
[x,y] 中无重的子区间数。
输出若干行,对于每一个询问,输出一个整数,表示你所求得的答案。
样例输入1
4 3
1 1 2 1
2 1 3
1 2 3
2 1 4
样例输出1
4
9
编 号 编号 编号 | 分 值 分值 分值 | n n n | m m m |
---|---|---|---|
1 1 1 | 10 10 10 | ≤ 500 \le 500 ≤500 | ≤ 500 \le500 ≤500 |
2 2 2 | 15 15 15 | ≤ 1000 \le 1000 ≤1000 | ≤ 1000 \le 1000 ≤1000 |
3 3 3 | 15 15 15 | ≤ 5000 \le 5000 ≤5000 | ≤ 5000 \le5000 ≤5000 |
4 4 4 | 20 20 20 | ≤ 50000 \le 50000 ≤50000 | ≤ 50000 \le 50000 ≤50000 |
5 5 5 | 20 20 20 | ≤ 100000 \le 100000 ≤100000 | ≤ 100000 \le 100000 ≤100000 |
6 | 20 20 20 | ≤ 200000 \le 200000 ≤200000 | ≤ 200000 \le 200000 ≤200000 |
对于全部的数据,保证 n 、 m ≤ 200000 n 、m\le 200000 n、m≤200000,保证任意时刻序列中的最大值 ≤ n \le n ≤n
YALI noip2020 模拟
首先声明,题目与莫队一点关系都没有。。。
(考场上与待修改莫队斗智斗勇 )
考虑不修改,那么只需要对每个左端点求出最大右端点。
对每个点求出权值下次出现的位置
n
e
x
[
i
]
nex[i]
nex[i]
那么最大右端点
R
[
i
]
=
m
i
n
(
n
e
x
[
j
]
)
(
i
≤
j
)
R[i]=min(nex[j])(i\leq j)
R[i]=min(nex[j])(i≤j) 即后缀最小值。
这时想到一道题楼房重建
我们在线段树中计算节点
p
p
p 的答案时,在
l
s
ls
ls 中线段树二分找到小于
m
i
n
n
[
r
s
]
minn[rs]
minn[rs] 的位置,小于的数用原数,大于就统计个数和
m
i
n
n
[
r
s
]
minn[rs]
minn[rs] 相乘即可。相当于把
p
u
s
h
u
p
pushup
pushup 魔改。
#include<bits/stdc++.h>
#define N 200005
#define A p<<1
#define B p<<1|1
typedef long long ll;
using namespace std;
inline int read() {
char ch;
while(!isdigit(ch=getchar()));
int sum=ch^48;
while(isdigit(ch=getchar()))sum=(sum<<1)+(sum<<3)+(ch^48);
return sum;
}
int a[N],nex[N];
set<int> po[N];set<int>::iterator it;
struct seg{
ll minn,ans;
}t[N<<2];
inline ll merge(int p,int l,int r,ll lim){
if(l==r)return min(t[p].ans,lim);
int mid=(l+r)>>1;
if(t[B].minn<=lim)return t[p].ans-t[B].ans+merge(B,mid+1,r,lim);
return merge(A,l,mid,lim)+lim*(r-mid);
}
inline void pushup(int p,int l,int r){
t[p].minn=min(t[A].minn,t[B].minn);
int mid=(l+r)>>1;
t[p].ans=merge(A,l,mid,t[B].minn)+t[B].ans;
}
inline void build(int p,int l,int r){
if(l==r){
t[p].ans=t[p].minn=nex[l];
return ;
}
int mid=(l+r)>>1;
build(A,l,mid);build(B,mid+1,r);
pushup(p,l,r);
}
inline void upda(int p,int l,int r,int tl,ll val){
if(l==r){
t[p].ans=t[p].minn=val;
return ;
}
int mid=(l+r)>>1;
if(tl<=mid)upda(A,l,mid,tl,val);
else upda(B,mid+1,r,tl,val);
pushup(p,l,r);
}
ll ans,Lim;
inline void ask(int p,int l,int r,int tl,int tr){
if(tl<=l&&r<=tr){
ans+=merge(p,l,r,Lim);
Lim=min(Lim,t[p].minn);
return ;
}
int mid=(l+r)>>1;
if(tr>mid)ask(B,mid+1,r,tl,tr);
if(tl<=mid)ask(A,l,mid,tl,tr);
}
int main(){
int n=read(),m=read();
for(int i=1;i<=n;++i){
a[i]=read();
po[i].insert(n+1);
}
for(int i=n;i;--i){
nex[i]=*(po[a[i]].begin());
po[a[i]].insert(i);
}
build(1,1,n);
while(m--){
int op=read();
if(op==1){
int x=read(),y=read();
it=po[a[x]].find(x);int ne=*(++it);--it;
if(it!=po[a[x]].begin()){--it;upda(1,1,n,*it,ne);}
po[a[x]].erase(x);it=po[y].lower_bound(x),a[x]=y;
upda(1,1,n,x,*it);
if(it!=po[y].begin())--it,upda(1,1,n,*it,x);
po[y].insert(x);
}else{
int x=read(),y=read();
ans=0;Lim=y+1;
ask(1,1,n,x,y);ans-=1ll*(x+y)*(y-x+1)/2;
printf("%lld\n",ans);
}
}
return 0;
}