Online Judge | Problem Set | Authors | Online Contests | User | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Web Board Home Page F.A.Qs Statistical Charts | Current Contest Past Contests Scheduled Contests Award Contest |
雁过留声——预处理+RMQ首先,可以很快速的获得以每个数i为左端点,区间的最右断点R[i], 方法是注意到R[i]=min(R[i+1],min{j|A[j]=A[i]}-1) 接下来,就是求一个区间[l,r]内的最长相异序列, 观察此时以i为左端点的序列的最大长度f(i): f(i)=min{R[i]-i+1,r-i+1}, 如果只对付R[i]-i+1或r-i+1,那么还是比较容易的,但那个取最小却太烦了。 怎么办? 继续深思: R[i]为单调的 那么... 存在x,使 for l<=i<=x f(i)=R[i]-i+1 for x<i<r f(i)=r-i+1 这是一个惊人的结论!换句话说,只要找到x,问题转化为一个 对R[i]-i+1的RMQ 那如何求x呢? 易知,x为最大的x使R[x]<r 呵呵,用二分查找x就可以了! 由于我比较懒,不想去默写rmq标准算法(或DC3)了, 就用传说中的“扩展树状数组”来实现。 关键代码: struct BIT{ int d[NMax]; BIT(){for (int i=0;i<NMax;i++)d[i]=0;} void ins(int a,int x){ for (d[a]=(d[a]<x?x:d[a]);a<N;a+=((a+1)&-(a+1))) if (d[a]<x)d[a]=x; } int ask(int b,int e){ int ret; for (ret=0;e>=0 && e-((e+1)&-(e+1))+1>=b;e-=((e+1)&-(e+1))) if (ret<d[e])ret=d[e]; return ret; } }bit; struct BIA{ int d[NMax]; BIA(){for (int i=0;i<NMax;i++)d[i]=0;} void ins(int a,int x){if (!a)return; for (d[a]=(d[a]<x?x:d[a]);a>0;a-=(a&-a)) if (d[a]<x)d[a]=x; } int ask(int b,int e){if (!b)b++;if (b>e)return 0; int ret; for (ret=0;b<N && b+(b&-b)-1<=e;b+=(b&-b)) if (ret<d[b])ret=d[b]; return ret; } }bia; int Ask(int b,int e){ int ret=bit.ask(b,e); int tmp=bia.ask(b,e); if (ret>tmp)return ret; return tmp; } for (int i=0;i<2000001;i++)last[i]=N; R[N-1]=N-1; last[A[N-1]+1000000]=N-1; for (int i=N-2;i>=0;i--){ R[i]=R[i+1]; if (last[A[i]+1000000]-1<R[i])R[i]=last[A[i]+1000000]-1; last[A[i]+1000000]=i; } for (int i=0;i<N;i++){ bit.ins(i,R[i]-i+1); bia.ins(i,R[i]-i+1); } if (R[b]>=e)printf("%d\n",e-b+1); else{ //find that R[l]<e l=b;r=e; while (r-l>1){ if (R[(r+l)>>1]<e)l=((r+l)>>1); else r=((r+l)>>1); } ret=Ask(b,l); if (ret<e-l)ret=e-l; printf("%d\n",ret); } Followed by: Post your reply here: |
All Rights Reserved 2003-2013 Ying Fuchen,Xu Pengcheng,Xie Di
Any problem, Please Contact Administrator