2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛

2019中国大学生程序设计竞赛(CCPC) - 网络选拔赛

B.array HDU - 6703

题意:给定一个数组 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,,an,每个数字都不相同。有两种操作,第一种把原数组的值加上 1 0 7 10^7 107。另一种操作,查询一个大于等于k的数,不在 a 1 a_1 a1 a r a_r ar之间出现
思路:当原数组的值变化后,那么在查询大于k的时候,是有可能查询到这个值的。比如把3加上了1e7,那么在右边查询的时候是有可能出现的答案3的

1、权值线段树维护下标最大值。我们原本要找的是下标不在[1,r]出现,值大于等于k的数。由于各个数都是不相同的,所以不用考虑等于的情况。建立了权值线段树之后,我们要查询的就是在[k+1,n]范围内,下标大于r的第一个数,注意是大于r的第一个数,而不是大于r下标最小的数。因为k在[1,n],所以答案也就是[1,n+1]的范围内。

代码1:

#include <bits/stdc++.h>
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define ll long long
using namespace std;
const int maxn=1e5+10,INF=0x3f3f3f3f;

int t,n,m,a[maxn],b[maxn];
int ST[maxn<<2];

void Push_up(int rt)
{
    ST[rt]=max(ST[ls],ST[rs]);
}

void Build(int rt,int L,int R)
{
    ST[rt]=0;
    if(L==R)
        return;
    int mid=(L+R)>>1;
    Build(ls,L,mid);
    Build(rs,mid+1,R);
}

void Update(int rt,int pos,int L,int R,int val)
{
    if(L==R)
    {
        ST[rt]=val;
        return;
    }
    int mid=(L+R)>>1;
    if(pos<=mid)
        Update(ls,pos,L,mid,val);
    if(pos>mid)
        Update(rs,pos,mid+1,R,val);
    Push_up(rt);
}

int Query(int rt,int l,int r,int L,int R,int index)
{
    if(L==R)
        return L;
    int mid=(L+R)>>1;
    int ans=INF;
    if(l<=mid&&index<ST[ls])
        ans=Query(ls,l,r,L,mid,index);
    if(ans!=INF)
        return ans;
    if(r>mid&&index<ST[rs])
        ans=Query(rs,l,r,mid+1,R,index);
    return ans;
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        a[n+1]=n+1;
        b[n+1]=n+1;
        n++;
        sort(b+1,b+1+n);
        int total=unique(b+1,b+1+n)-b-1;

        Build(1,1,total);

        for(int i=1;i<=n;++i)
        {
            int pos=lower_bound(b+1,b+1+total,a[i])-b;
            Update(1,pos,1,n,i);
        }

        int ans=0;
        while(m--)
        {
            int op;
            scanf("%d",&op);
            if(op==1)
            {
                int t1;
                scanf("%d",&t1);
                int pos=t1^ans;
                int p=lower_bound(b+1,b+1+total,a[pos])-b;
                Update(1,p,1,n,INF);
            }
            else
            {
                int t2,t3;
                scanf("%d%d",&t2,&t3);
                int r=t2^ans,k=t3^ans;
                ans=Query(1,k,n,1,n,r);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

2、主席树。把改变的数放到set里,因为此时查询大于等于k的数,变大的这个数的原数,已经不再下标[1,r]的范围内了。那么这个数,就有可能成为大于等于k的最小的那个数。举个例子,数组是:3,4,1,2,5。现在把3这个数变大,就变成了3+1e7,4,1,2,5。那么这时查询 r=3,k=3的答案就是3。如果是在原数组,r=3,k=3的答案就是5

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10,INF=0x3f3f3f3f;

int t,n,m;
int a[maxn],b[maxn];

int root[maxn],ls[maxn*30],rs[maxn*30],ST[maxn*30],no;

int Build(int L,int R)
{
    int rt=++no;
    if(L==R)
        return rt;
    int mid=(L+R)>>1;
    ls[rt]=Build(L,mid);
    rs[rt]=Build(mid+1,R);
    return rt;
}

int Update(int pre,int pos,int L,int R)
{
    int rt=++no;
    ST[rt]=ST[pre]+1;
    ls[rt]=ls[pre];
    rs[rt]=rs[pre];
    if(L==R)
        return rt;
    int mid=(L+R)>>1;
    if(pos<=mid)
        ls[rt]=Update(ls[pre],pos,L,mid);
    if(pos>mid)
        rs[rt]=Update(rs[pre],pos,mid+1,R);
    return rt;
}

int Query(int pre,int now,int p,int L,int R)
{
    if(ST[now]-ST[pre]==0)
        return INF;
    if(L==R)
        return L;
    int mid=(L+R)>>1;
    int ans=INF;
    if(p<=mid)
        ans=Query(ls[pre],ls[now],p,L,mid);
    if(ans!=INF)
        return ans;
    return Query(rs[pre],rs[now],p,mid+1,R);
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        set<int> s;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        a[n+1]=n+1;
        b[n+1]=n+1;
        n++;
        sort(b+1,b+1+n);
        int total=unique(b+1,b+1+n)-b-1;

        no=0;
        root[0]=Build(1,total);

        for(int i=1;i<=n;++i)
        {
            int pos=lower_bound(b+1,b+1+total,a[i])-b;
            root[i]=Update(root[i-1],pos,1,total);
        }

        int ans=0;
        while(m--)
        {
            int op;
            scanf("%d",&op);
            if(op==1)
            {
                int t1;
                scanf("%d",&t1);
                int pos=t1^ans;
                s.insert(a[pos]);
            }
            else
            {
                int t2,t3;
                scanf("%d%d",&t2,&t3);
                int r=t2^ans,k=t3^ans;
                int pos=lower_bound(b+1,b+1+total,k)-b;
                ans=Query(root[r],root[n],pos,1,n);
                auto it=s.lower_bound(k);
                if(it!=s.end())
                    ans=min(ans,*it);
                printf("%d\n",ans);
            }
        }
    }
    return 0;
}

以前写的代码

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <set>
#include <map>
#include <cstring>
#include <string>
#include <cmath>
#define rep(i,a,b) for (int i=a; i<=b; ++i)
#define per(i,b,a) for (int i=b; i>=a; --i)
#define mes(a,b)  memset(a,b,sizeof(a))
#define mp make_pair
#define ll long long
#define pb push_back
#define pii pair<int,int>
#define pll pair<ll,ll>
#define isZero(d)  (abs(d) < 1e-8)
using namespace std;
const int maxn=1e5+5,INF=0x3f3f3f3f;
const int mod=1e9+7;

int T,n,m,total,a[maxn],b[maxn],version[maxn]; 
int t1,t2,t3;
int ST[maxn*40],ls[maxn*40],rs[maxn*40],no;
set<int> s;

int Build(int L,int R)
{
    int rt=++no;
    ST[rt]=0;
    if(L==R)
        return rt;
    int mid=(L+R)>>1;
    ls[rt]=Build(L,mid);
    rs[rt]=Build(mid+1,R);
    return rt; 
}

int Update(int pre,int p,int L,int R)
{
    int rt=++no;
    ls[rt]=ls[pre];
    rs[rt]=rs[pre];
    ST[rt]=ST[pre]+1;
    if(L==R)
        return rt;
    int mid=(L+R)>>1;
    if(p<=mid)
        ls[rt]=Update(ls[pre],p,L,mid);
    if(p>mid)
        rs[rt]=Update(rs[pre],p,mid+1,R);
    return rt;
}

int Query(int pre,int now,int p,int L,int R)
{
    if(L==R)
        return L;
    int mid=(L+R)>>1;
    int cnt1=ST[ls[now]]-ST[ls[pre]],cnt2=ST[rs[now]]-ST[rs[pre]];
    int ans=INF;
    if(p<=mid&&cnt1)
        ans=Query(ls[pre],ls[now],p,L,mid);
    if(ans!=INF)
        return ans;
    if(cnt2)
        ans=Query(rs[pre],rs[now],p,mid+1,R); 
    return ans;
}

int main()
{
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&n,&m);
        rep(i,1,n)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        a[n+1]=n+1;
        b[n+1]=n+1;
        n++;
        sort(b+1,b+1+n);
        total=unique(b+1,b+1+n)-b-1;
    	//初始化    
        no=0;
        version[0]=Build(1,total);
 		//其实不需要离散化,不过我习惯了
        for(int i=1;i<=n;++i)
        {
            int p=lower_bound(b+1,b+1+total,a[i])-b;
            version[i]=Update(version[i-1],p,1,total);
        }
        ll ans=0;
        s.clear();//初始化啊!!泪两行
        while(m--)
        {
            int op;
            scanf("%d",&op);
            if(op==1)
            {
                scanf("%d",&t1);
                int  pos=ans^t1;
                s.insert(a[pos]);
            }
            else
            {
                scanf("%d%d",&t2,&t3);
                int r=ans^t2,k=ans^t3;
                int pos=lower_bound(b+1,b+1+n,k)-b;
                auto it=s.lower_bound(k);    
                //printf("pos=%d k=%d\n",pos,k);    
                ans=Query(version[r],version[n],pos,1,total);
                if(it!=s.end())
                    ans=min(ans,1ll*(*it));
                printf("%lld\n",ans);
            }
        }   
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值