P6617 查找 Search 线段树 查找区间内是否有两个和为w的数(w不变)
Posted 昵称很长很长真是太好了
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P6617 查找 Search 线段树 查找区间内是否有两个和为w的数(w不变)相关的知识,希望对你有一定的参考价值。
题解:
每个点x,设置其前驱为离其最近的w-x的位置
每次修改可能影响O(n)个位置:
w-x x x x x x x…
这样后面每个位置的前驱都是w-x
如果修改了w-x的值,这样会导致O(n)个修改
注意到这个是存在性判定
如果存在两个
(
i
1
,
j
1
)
,
(
i
2
,
j
2
)
(i_1,j_1),(i_2,j_2)
(i1,j1),(i2,j2)使得
a
[
i
1
]
+
a
[
j
1
]
=
w
,
a
[
i
2
]
+
a
[
j
2
]
=
w
a[i_1]+a[j_1]=w,a[i_2]+a[j_2]=w
a[i1]+a[j1]=w,a[i2]+a[j2]=w,而且
[
i
2
,
j
2
]
[i_2,j_2]
[i2,j2]包含了
[
i
1
,
j
1
]
[i_1,j_1]
[i1,j1],则
(
i
2
,
j
2
)
(i_2,j_2)
(i2,j2)没有任何意义
这样每个点只存在
O
(
1
)
O(1)
O(1)个配对关系
由于是存在性,所以我们维护b[i]表示每个点的前驱
如果区间
[
l
,
r
]
[l,r]
[l,r]内
b
[
i
]
b[i]
b[i]最大值在
[
l
,
r
]
[l,r]
[l,r]中,则存在,否则不存在
这样只需要线段树求最大值,和multiset维护前驱后继即可
—来自李欣隆大师的讲解。
代码:
#include<bits/stdc++.h>
#define endl '\\n'
//#define int long long
using namespace std;
const int maxn=5e5+10;
bool previsited[maxn],sufvisited[maxn];
multiset<int> ss[maxn];
int pre[maxn],suf[maxn];
int a[maxn];
int imax[maxn<<2];
void update(int node,int start,int ends,int pos,int val){
if(start==ends){
imax[node]=val;
return ;
}
int mid=(start+ends)>>1;
if(pos<=mid) update(node<<1,start,mid,pos,val);
else update(node<<1|1,mid+1,ends,pos,val);
imax[node]=max(imax[node<<1],imax[node<<1|1]);
}
int query(int node,int start,int ends,int l,int r){
if(l<=start&&ends<=r){
return imax[node];
}
int mid=(start+ends)>>1;
int res=0;
if(l<=mid) res=max(res,query(node<<1,start,mid,l,r));
if(mid<r) res=max(res,query(node<<1|1,mid+1,ends,l,r));
return res;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n,m,w;
cin>>n>>m>>w;
for(int i=1;i<=n;i++){
cin>>a[i];
if(ss[w-a[i]].size()!=0){
set<int>::iterator it=ss[w-a[i]].end();
it--;
if(!sufvisited[*it]){
sufvisited[*it]=true;
previsited[i]=true;
pre[i]=*it;
suf[*it]=i;
update(1,1,n,i,pre[i]);
}
}
ss[a[i]].insert(i);
}
int cnt=0;
for(int i=1;i<=m;i++){
int opt;
cin>>opt;
if(opt==1){
int x,y;
cin>>x>>y;
if(a[x]==y) continue;
ss[a[x]].erase(x);
ss[y].insert(x);
sufvisited[x]=false;
previsited[x]=false;
if(pre[x]!=0) sufvisited[pre[x]]=false;
if(suf[x]!=0) previsited[suf[x]]=false;
update(1,1,n,x,0);
if(suf[x]) update(1,1,n,suf[x],0);
if(pre[x]!=0){ //修改前驱结点
set<int>::iterator it=ss[w-a[pre[x]]].upper_bound(pre[x]);
suf[pre[x]]=0;
if(it!=ss[w-a[pre[x]]].end()&&!previsited[*it]){
//cout<<"now "<<x<<endl;
previsited[*it]=true;
sufvisited[pre[x]]=true;
suf[pre[x]]=*it;
pre[*it]=pre[x];
update(1,1,n,*it,pre[*it]);
//cout<<"debug "<<endl;
}
else{
}
}
if(suf[x]!=0){ //修改后驱节点
set<int>::iterator it=ss[w-a[suf[x]]].lower_bound(suf[x]);
pre[suf[x]]=0;
if(it!=ss[w-a[suf[x]]].begin()){
it--;
if(*it<suf[x]&&!sufvisited[*it]){
sufvisited[*it]=true;
previsited[suf[x]]=true;
pre[suf[x]]=*it;
suf[*it]=suf[x];
update(1,1,n,suf[x],pre[suf[x]]);
//cout<<"debug "<<endl;
}
}
}
a[x]=y;
pre[x]=suf[x]=0;
set<int>::iterator it=ss[w-a[x]].lower_bound(x);
if(it!=ss[w-a[x]].begin()){ //求改结点本身(向前连)
it--;
//cout<<"debug "<<*it<<endl;
if(*it<x){
if(!sufvisited[*it]){
//cout<<"debug "<<*it<<endl;
sufvisited[*it]=true;
previsited[x]=true;
pre[x]=*it;
suf[*it]=x;
update(1,1,n,x,pre[x]);
//cout<<"update "<<x<<" "<<pre[x]<<endl;
}
else {
if(suf[*it]>x){
pre[suf[*it]]=0;
previsited[suf[*it]]=false;
pre[x]=*it;
suf[*it]=x;
previsited[x]=true;
update(1,1,n,x,*it);
}
}
}
}
it=ss[w-a[x]].upper_bound(x); //修改结点本身(向后连)
if(it!=ss[w-a[x]].end()){
if(!previsited[*it]){
previsited[*it]=true;
sufvisited[x]=true;
pre[*it]=x;
suf[x]=*it;
update(1,1,n,suf[x],pre[suf[x]]);
}
else{
if(pre[*it]<x){
suf[pre[*it]]=0;
sufvisited[pre[*it]]=false;
pre[*it]=x;
sufvisited[x]=true;
suf[x]=*it;
update(1,1,n,*it,x);
}
}
}
}
else{
int l,r;
cin>>l>>r;
l=l^cnt,r=r^cnt;
int ans=query(1,1,n,l,r);
//cout<<"debug "<<l<<" "<<r<<" "<<ans<<endl;
if(ans>=l){
cnt++;
cout<<"Yes"<<endl;
}
else cout<<"No"<<endl;
}
}
}
以上是关于P6617 查找 Search 线段树 查找区间内是否有两个和为w的数(w不变)的主要内容,如果未能解决你的问题,请参考以下文章