2019CCPC网络选拔赛 array(权值线段树)
Posted kkkek
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019CCPC网络选拔赛 array(权值线段树)相关的知识,希望对你有一定的参考价值。
题目大意:
T个样例。给你一个长度为n的数组a,1≤a[i]≤n,a[i]各不相同。m个操作。ans初始为0。有两种操作:
操作1:给你t1。pos=t1^ans。把数组下标为pos的数,数值+1e7;
操作2:给你t2,t3。r=t2^ans,k=t3^ans。输出**与数组下标1~r的数不同**且**不小于k**的最小数。更新ans。
数据范围:
1≤T≤10,1≤n≤1e5,1≤m≤1e5,1≤a[i]≤n,a[i]各不相同,1≤pos≤n,1≤r≤n,1≤k≤n。
∑n≤510,000,∑m≤510,000。
赛后补题。
此处照搬标准题解:
因为数组中的值唯一,且在1到n的范围内,而询问的r和k也在1到n的范围内。 所以对于任意一个被操 作1修改过的值都不会成为询问的答案,而询问的结果也必然在k到n+1的范围内。 因为没有被修改过 值是唯一的,所以可以建立权值线段树,维护权值区间内的值所在下标的最大值。而询问则转化为不小 于k的值里面,下标超过r的最小权值是多少。 如何处理询问呢,一种较为暴力的解法是直接在线段树上 询问权值在k到n+1的范围内第一个下标超过r的权值是多少。但复杂度可能会被卡,需要减枝。 再加上 一个额外的判断就可以了,就是在递归查询完左子树内存不存在大于r的下标之后,如果不存在,则先 看一下右子树内的下标的最大值是否大于r。如果不大于r,则不必再进入右子树内查询,否则答案一定 在右子树内。在进左子树之前也利用同样的判断条件来判断是否有必要进入左子树,这样做可以保证单 次查询的复杂度是O(log n) 的。 而对于操作1,则等价于修改某个权值的下标为无穷大。操作复杂度也 是O(log n )的。 综上所述,得到了一个复杂度为O( m * log n )的在线算法,可以较快地通过此题。
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+50; const int inf=0x3f3f3f3f; struct P int l,r,id; tree[maxn<<2]; void build(int l,int r,int k) tree[k].l=l;tree[k].r=r; if(tree[k].l==tree[k].r) tree[k].id=0; return ; int mid=(tree[k].l+tree[k].r)/2; build(l,mid,k*2); build(mid+1,r,k*2+1); tree[k].id=0; void add(int x,int id,int k) if(tree[k].l==tree[k].r) tree[k].id=id;return; int mid=(tree[k].l+tree[k].r)/2; if(x<=mid)add(x,id,k*2); else add(x,id,k*2+1); tree[k].id=max(tree[k*2].id,tree[k*2+1].id); int find(int l,int r,int k,int minid) if(tree[k].l==tree[k].r) if(tree[k].id>minid)return l; return inf; if(tree[k].id<=minid)return inf; int mid=(tree[k].l+tree[k].r)/2; int id1=tree[k*2].id,id2=tree[k*2+1].id; if(r<=mid) if(id1<=minid)return inf; return find(l,r,k*2,minid); if(l>mid) if(id2<=minid)return inf; return find(l,r,k*2+1,minid); int ans1=inf,ans2=inf,ans=inf; if(id1>minid)ans1=find(l,mid,k*2,minid); if(id2>minid&&ans1==inf)ans2=find(mid+1,r,k*2+1,minid); ans=min(ans1,ans2); return ans; /*void f(int l,int r,int k) printf("##l:%d ##r:%d##id:%d\n",tree[k].l,tree[k].r,tree[k].id); if(tree[k].l==tree[k].r)return; int mid=(tree[k].l+tree[k].r)/2; f(l,mid,k*2); f(l,mid,k*2+1); */ int main() int T; scanf("%d",&T); while(T--) int n,m,op,t1,t2,t3,i,a[maxn],ans=0; scanf("%d%d",&n,&m); build(1,n,1); for(i=1;i<=n;i++) scanf("%d",&a[i]); add(a[i],i,1); //f(1,n,1); for(i=1;i<=m;i++) scanf("%d",&op); if(op==1) scanf("%d",&t1); add(a[t1^ans],inf,1); //f(1,n,1); continue; scanf("%d%d",&t2,&t3); t2^=ans;t3^=ans; ans=find(t3,n,1,t2); //printf("@@r:%d k:%d@@ans:",t2,t3); if(ans!=inf)printf("%d\n",ans); else ans=n+1; printf("%d\n",ans); //puts("############");
以上是关于2019CCPC网络选拔赛 array(权值线段树)的主要内容,如果未能解决你的问题,请参考以下文章
[HDU 6447][YJJ's Salesman][2018CCPC网络选拔赛 1010][离散化+线段树+DP]
hdoj6703 2019 CCPC网络选拔赛 1002 array
hdu6075 2019CCPC网络选拔赛1004 path