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不变)的主要内容,如果未能解决你的问题,请参考以下文章

线段树——快速区间查找

线段树的思路

使用线段树查找重叠区间的总长度?

培训补坑(day7:线段树的区间修改与运用)(day6是测试,测试题解以后补坑QAQ)

线段树2 求区间最小值

POJ3264线段树求最值