P6617 查找 Search 题解

P6617 查找 Search 题解

真的肝死我了,这破题调了好长时间。还是颇有思维量的一道题。

解法

引入

我这道题的思路源自 P5278 那题。那道题可转换为若干子问题,其中之一就是让我们求区间内有无重复数字。

我们可以维护对于每个位置左边第一个与该位置值相同的位置,记作 p r e i pre_i prei。如果不存在该位置,则记为 − 1 -1 1

[ l , r ] [l,r] [l,r] 中存在相等的数字当且仅当 ∃ x ∈ [ l , r ] \exist x \in [l,r] x[l,r],使得 l ≤ p r e x l \le pre_x lprex

显然初始化这个 p r e pre pre 数组可以做到线性,现在考虑如何维护修改。

我们考虑维护一个集合 s i s_i si,其中包含所有值等于 i i i 的位置。如果将 a x a_x ax 的值修改为 y y y,这个操作即为在 s a x s_{a_x} sax 中删除 x x x,在 s y s_y sy 中插入 x x x。这个过程最多只影响 3 3 3 个位置的 p r e pre pre,即为 x x x x x x 右面第一个等于 a x a_x ax 的位置, x x x 右面第一个等于 y y y 的位置。过程如图,黑笔为原来的 p r e pre pre 情况,红笔为修改后的 p r e pre pre 情况。字有点丑,见笑了。

作图

迁移

将那道题的思想继承到这道题上。

还是维护数组 p r e pre pre,但是意义有所不同。这里的 p r e pre pre 维护的是每个位置左边第一个与该位置值相加等于 w w w 的位置。如果不存在该位置,则记为 − 1 -1 1

需要注意的是,我们这次维护的 p r e pre pre 需要满足 ∀ i , j ∈ [ 1 , n ] , p r e i ≠ p r e j \forall i,j \in [1,n],pre_i \neq pre_j i,j[1,n],prei=prej。否则我们的时间复杂度是错的。如图,假若我们将所有的红色点的 p r e pre pre 都设为绿色点,那么当绿色点的值修改时我们需要修改每个红色点的 p r e pre pre 值。这么修改时间复杂度可以是 O ( n ) \mathcal{O}(n) O(n) 的。

作图

我们可以改进这个问题。因为如果有一个位置 x ∈ [ l , r ] x \in [l,r] x[l,r] 满足 p r e x ∈ [ l , r ] pre_x \in [l,r] prex[l,r],那对于 k ≥ x , p r e k = p r e x k \ge x,pre_k = pre_x kx,prek=prex,这些 p r e k pre_k prek 都是无效的,不会对答案产生影响。相反,如果有一个位置 x ∈ [ l , r ] x \in [l,r] x[l,r] 不满足 p r e x ∈ [ l , r ] pre_x \in [l,r] prex[l,r],那对于 k ≥ x , p r e k = p r e x k \ge x,pre_k = pre_x kx,prek=prex,这些 p r e k pre_k prek 也都是无效的,不会对答案产生影响。所以对于每个值,最多只会有一个 p r e pre pre 值等于它。这些无效的值设为 − 1 -1 1 即可。

我们可以额外维护一个 e d ed ed 数组来维护这个值是否已经“被使用”。

这里我们只需要考虑在每次修改中需要更改的位置有哪些。如图,红色点即为可能需要修改的值。最多有 5 5 5 个。

作图

知道了该修改哪些点了,我们就可以比较暴力地修改了。对于每个需要修改的位置 x x x,我们先把 e d p r e x ed_{pre_x} edprex 给解除一下,因为我们后续要进行修改。然后在 s a x s_{a_x} sax 中查找 x x x 左边第一个与该位置值相同的位置,进行修改即可。

线段树只是个辅助维护 p r e pre pre 的工具,在本题中不是重点,不过多介绍了。线段树维护区间 p r e pre pre 最大值。

代码

#include<bits/stdc++.h>
namespace fast_IO
{
	#define Getchar() p1==p2 and (p2=(p1=Inf)+fread(Inf,1,1<<21,stdin),p1==p2)?EOF:*p1++
	#define Putchar(c) p3==p4 and (fwrite(Ouf,1,1<<21,stdout),p3=Ouf),*p3++=c
	char Inf[1<<21],Ouf[1<<21],*p1,*p2,*p3=Ouf,*p4=Ouf+(1<<21);
	inline void read(int &x,char c=Getchar())
	{
		bool f=c!=45;
		x=0;
		while(c<48 or c>57) c=Getchar(),f&=c!=45;
		while(c>=48 and c<=57) x=(x<<3)+(x<<1)+(c^48),c=Getchar();
		x=f?x:-x;
	}
	inline void write(int x)
	{
		if(x<0) Putchar(45),x=-x;
		if(x>=10) write(x/10),x%=10;
		Putchar(x^48);
	}
	inline void write(std::string st)
	{
		for(int i=0;i<st.size();i++) Putchar(st[i]);
	}
};
using namespace fast_IO;
int n,m,w,a[500010],pre[500010],ed[500010];
std::unordered_map< int,std::set<int> > mp;
struct node
{
	int mpre;
	node *lc,*rc;
	inline void pushup()
	{
		mpre=std::max(lc->mpre,rc->mpre);
	}
};
class seg_tree
{
	#define ls l,mid
	#define rs mid+1,r
private:
	node *root;
	inline node *build(int l,int r)
	{
		node *rt=new node;
		if(l==r) rt->mpre=pre[l];
		else
		{
			int mid=(l+r)/2;
			rt->lc=build(ls),rt->rc=build(rs),rt->pushup();
		}
		return rt;
	}
	inline void fix(node *rt,const int &pos,const int &val,int l,int r)
	{
		if(l==r)
		{
			rt->mpre=val;
			return;
		}
		int mid=(l+r)/2;
		if(pos<=mid) fix(rt->lc,pos,val,ls);
		else fix(rt->rc,pos,val,rs);
		rt->pushup();
	}
	inline int ask(node *rt,const int &L,const int &R,int l,int r)
	{
		if(L<=l && r<=R) return rt->mpre;
		int mid=(l+r)/2,ret=0;
		if(L<=mid) ret=std::max(ret,ask(rt->lc,L,R,ls));
		if(R>mid) ret=std::max(ret,ask(rt->rc,L,R,rs));
		return ret;
	}
public:
	inline void build()
	{
		root=build(1,n);
	}
	inline void fix(const int &pos,const int &val)
	{
		fix(root,pos,val,1,n);
	}
	inline int ask(const int &L,const int &R)
	{
		return ask(root,L,R,1,n);
	}
};
seg_tree tree;
std::vector< std::pair<int,int> > v;
inline void addget(const int &val,const int &it)
{
	if(pre[it]!=-1) ed[pre[it]]=0;
	v.push_back(std::make_pair(val,it));
}
inline void getpre(const int &val,const int &it)
{
	if(mp[w-val].empty())
	{
		pre[it]=-1,tree.fix(it,pre[it]);
		return;
	}
	std::set<int>::iterator it2=mp[w-val].lower_bound(it);
	if(it2==mp[w-val].begin()) pre[it]=-1,tree.fix(it,pre[it]);
	else
	{
		it2--;
		if(ed[*it2])
		{
			if(ed[*it2]>=it) pre[ed[*it2]]=-1,tree.fix(ed[*it2],pre[ed[*it2]]),ed[*it2]=it,pre[it]=*it2,tree.fix(it,pre[it]);
			else pre[it]=-1,tree.fix(it,pre[it]);
		}else ed[*it2]=it,pre[it]=*it2,tree.fix(it,pre[it]);
	}
}
inline void deladd()
{
	for(int i=0;i<v.size();i++) getpre(v[i].first,v[i].second);
	v.clear();
}
int main()
{
	// freopen("example.in","r",stdin);
	// freopen("example.out","w",stdout);
	read(n),read(m),read(w);
	for(int i=1;i<=n;i++) read(a[i]);
	std::set<int>::iterator it1,it2,it3;
	for(int i=1,prev;i<=n;i++)
	{
		if(!mp[w-a[i]].size()) pre[i]=-1;
		else
		{
			prev=*(--mp[w-a[i]].end());
			if(ed[prev]) pre[i]=-1;
			else pre[i]=prev,ed[prev]=i;
		}
		mp[a[i]].insert(i);
	}
	tree.build();
	for(int i=1,op,l,r,ans=0;i<=m;i++)
	{
		read(op),read(l),read(r);
		if(op==1)
		{
			it1=mp[a[l]].find(l),it2=mp[w-a[l]].lower_bound(l);
			if(it2!=mp[w-a[l]].end()) addget(w-a[l],*it2);
			if(it1!=mp[a[l]].end())
			{
				it1++;
				if(it1!=mp[a[l]].end()) addget(a[l],*it1);
			}
			if(pre[l]!=-1) ed[pre[l]]=0;
			mp[a[l]].erase(l),mp[r].insert(l),a[l]=r;
			it1=mp[a[l]].find(l),it2=mp[w-a[l]].lower_bound(l),addget(a[l],*it1);
			if(it2!=mp[w-a[l]].end()) addget(w-a[l],*it2);
			if(it1!=mp[a[l]].end())
			{
				it1++;
				if(it1!=mp[a[l]].end()) addget(a[l],*it1);
			}
			deladd();
		}else
		{
			l^=ans,r^=ans;
			if(tree.ask(l,r)>=l) write("Yes\n"),ans++;
			else write("No\n");
		}
	}
	fwrite(Ouf,1,p3-Ouf,stdout),fflush(stdout);
	return 0;
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值