DTOJ#5232. 莫队

传送门

今天,接触信息学不久的小 A 刚刚学习了莫队。

莫队可以解决一类难以合并,但方便插入的信息维护。比如,给定一个序列,支持单点修改,每次询问一个区间出现了多少种数字。再比如,给定一个序列,支持单点修改,每次询问区间众数。诸如此类。

A 觉得这样的情况太平凡了。于是,他定义了一个区间是无重的,当且仅当区间内没有重复的数字。具体的,一个区间 [ l , r ] [l,r] [l,r] 无重,当且仅当, ∀ l ≤ i < j ≤ r \forall l\le i< j \le r li<jr,都有 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 nm,分别表示序列的长度,以及操作总数。

​第二行有 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 nm200000,保证任意时刻序列中的最大值 ≤ 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])(ij) 即后缀最小值。
这时想到一道题楼房重建
我们在线段树中计算节点 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值