因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大。如果找不到这样的数,则直接输出0。我会采取一些措施强制在线。
标签:
Orz zyf教给蒟蒻做法
蒟蒻并不会这题正解……(可持久化树套树?。。。Orz
对于每个点,我们可以求出pre[i],nex[i],那么询问的答案就是:求max (a[i]),其中 i 满足$ ( pre[i]<ql \ and \ nex[i]>qr\ and\ i \in [ql,qr] ) $
然后我们以(i,pre[i],nex[i])为坐标……将所有点抽象到三维空间中,每次查询就相当于是一次区域求最值!
这题我的感受:
因为前面做了两道区域求和的……然后思路不由自主又代入到搞【子树最大值】来更新答案……然而忘记了单点更新,也就是:虽然这个子树不合法,但是这一个点(根)还是可能合法的……
然后就是:KD-Tree如果可以搞整个子树的话,那么用整个子树的最值去更新,会优化很多……?
终于1A了一道KD-Tree啦~好开心(虽然不是自己想出的做法……)
一个更加优秀的做法:http://www.cnblogs.com/mhy12345/p/4517347.html
1 /************************************************************** 2 Problem: 3489 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:3712 ms 7 Memory:7920 kb 8 ****************************************************************/ 9 10 //BZOJ 3489 11 #include<cstdio> 12 #include<cstring> 13 #include<cstdlib> 14 #include<iostream> 15 #include<algorithm> 16 #define rep(i,n) for(int i=0;i<n;++i) 17 #define F(i,j,n) for(int i=j;i<=n;++i) 18 #define D(i,j,n) for(int i=j;i>=n;--i) 19 #define pb push_back 20 using namespace std; 21 typedef long long LL; 22 inline int getint(){ 23 int r=1,v=0; char ch=getchar(); 24 for(;!isdigit(ch);ch=getchar()) if (ch==‘-‘) r=-1; 25 for(; isdigit(ch);ch=getchar()) v=v*10-‘0‘+ch; 26 return r*v; 27 } 28 const int N=100010,INF=1e9; 29 /*******************template********************/ 30 int n,m,D,a[N],root,ans,now[N],nex[N],pre[N]; 31 struct node{ 32 int d[3],mn[3],mx[3],l,r,v,vmax; 33 int& operator [] (int i) {return d[i];} 34 }t[N]; 35 //d[0] 下标 36 //d[1] nex 37 //d[2] pre 38 bool operator < (node a,node b){return a[D]<b[D];} 39 40 #define L t[o].l 41 #define R t[o].r 42 #define mid (l+r>>1) 43 void Push_up(int o){ 44 F(i,0,2){ 45 t[o].mn[i]=min(t[o][i],min(t[L].mn[i],t[R].mn[i])); 46 t[o].mx[i]=max(t[o][i],max(t[L].mx[i],t[R].mx[i])); 47 } 48 t[o].vmax=max(t[o].v,max(t[L].vmax,t[R].vmax)); 49 } 50 51 int build(int l,int r,int dir){ 52 D=dir; 53 nth_element(t+l,t+mid,t+r+1); 54 int o=mid; 55 L = l < mid ? build(l,mid-1,(dir+1)%3) : 0; 56 R = mid < r ? build(mid+1,r,(dir+1)%3) : 0; 57 Push_up(o); 58 return o; 59 } 60 int ql,qr; 61 inline bool check(int o){ 62 if (!o) return 0; 63 if (t[o].mx[1]<=qr || t[o].mn[2]>=ql) return 0; 64 if (t[o].mn[0]>qr || t[o].mx[0]<ql) return 0; 65 return 1; 66 } 67 void query(int o){ 68 if (!o) return; 69 if (t[o].mn[0]>=ql && t[o].mx[0]<=qr && t[o].mn[1]>qr && t[o].mx[2]<ql){ 70 ans=max(ans,t[o].vmax); 71 return; 72 } 73 if (t[o][0]>=ql && t[o][0]<=qr && t[o][1]>qr && t[o][2]<ql) 74 ans=max(ans,t[o].v); 75 if (t[L].vmax>t[R].vmax){ 76 if (t[L].vmax>ans && check(L)) query(L); 77 if (t[R].vmax>ans && check(R)) query(R); 78 }else{ 79 if (t[R].vmax>ans && check(R)) query(R); 80 if (t[L].vmax>ans && check(L)) query(L); 81 } 82 } 83 int main(){ 84 #ifndef ONLINE_JUDGE 85 freopen("3489.in","r",stdin); 86 freopen("3489.out","w",stdout); 87 #endif 88 F(i,0,2) t[0].mn[i]=INF,t[0].mx[i]=-INF; 89 t[0].vmax=-1; 90 n=getint(); m=getint(); 91 F(i,1,n) a[i]=getint(); 92 F(i,1,n){ 93 pre[i]=now[a[i]]; 94 now[a[i]]=i; 95 } 96 F(i,1,n) nex[pre[i]]=i; 97 F(i,1,n) if (!nex[i]) nex[i]=n+1; 98 F(i,1,n) t[i][0]=i,t[i][1]=nex[i],t[i][2]=pre[i],t[i].v=a[i]; 99 root=build(1,n,0); 100 F(i,1,m){ 101 int x=getint(),y=getint(); 102 ql=min( (x+ans)%n+1,(y+ans)%n+1); 103 qr=max( (x+ans)%n+1,(y+ans)%n+1); 104 ans=0; 105 query(root); 106 printf("%d\n",ans); 107 } 108 return 0; 109 }
因为是OJ上的题,就简单点好了。给出一个长度为n的序列,给出M个询问:在[l,r]之间找到一个在这个区间里只出现过一次的数,并且要求找的这个数尽可能大。如果找不到这样的数,则直接输出0。我会采取一些措施强制在线。
第一行为两个整数N,M。M是询问数,N是序列的长度(N<=100000,M<=200000)
第二行为N个整数,描述这个序列{ai},其中所有1<=ai<=N
再下面M行,每行两个整数x,y,
询问区间[l,r]由下列规则产生(OIER都知道是怎样的吧>_<):
l=min((x+lastans)mod n+1,(y+lastans)mod n+1);
r=max((x+lastans)mod n+1,(y+lastans)mod n+1);
Lastans表示上一个询问的答案,一开始lastans为0
一共M行,每行给出每个询问的答案。
注意出题人为了方便,input的第二行最后多了个空格。
【BZOJ】【3489】A simple rmq problem
标签:
原文地址:http://www.cnblogs.com/Tunix/p/4522925.html