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;
}