NOI模拟(3.3)螺旋序列(出题人一定是月厨)
Description
S也想寻求真正的智慧,然而由于“抑制力”的存在,她必须先解决一系列询
问。
有一个长度为n的序列a,一个长度为m序列b被称为螺旋序列当且仅当
b1=bm且对于1<=i<=m有bi<=b1。
S需要回答q个询问,每个询问用l,r两个参数描述,表示询问区间[l,r]的最长
连续子螺旋序列的长度。
Input
第一行两个整数n,q,表示序列长度和询问数。
第二行n个整数ai表示序列a。
以下q行,每行两个整数l,r表示一次询问。
Output
对每次询问输出一行一个整数表示最大连续螺旋序列的长度。
Sample Input
5 3
3 2 1 2 3
1 4
2 5
1 5
Sample Output
3
3
5
Data Constraint
本题采用捆绑测试,只有通过一个子任务的全部数据才能得到该子任务分
数,否则不得分。
子任务1(20分):n,m<=10
子任务2(20分):n,m<=1000
子任务3(20分):n,m<=2*10^5,1<=ai<=10
子任务4(40分):n,m<=5*10^5;|ai|<=10^9
Solution
30分的裸暴力
离散化相等的数字,做一个链表,st表处理区间最大值,对每个询问都暴力去跳
#include <map> #include <vector> #include <stdio.h> #include <string.h> #include <algorithm> #define rg register template<class T> inline T dma(rg T x,rg T y) {return x>y?x:y;} template<class T> inline void read(rg T &x) { rg int c=getchar();rg bool b=0; for(;c<48||c>57;c=getchar()) if(c==45)b=1; for(x=0;c>47&&c<58;c=getchar()) x=(x<<1)+(x<<3)+c-48; if(b)x=-x; } const int N=500010; std::vector<int>vc; std::map<int,int>hc; int n,q,seq[N],fir[N],nex[N],st[20][N],bin[20],lgg[20],ans; void sequence_table() { lgg[0]=-1;for(rg int i=1;i<=n;i++)lgg[i]=lgg[i>>1]+1; for(rg int i=0;i<20;i++)bin[i]=1<<i; for(rg int i=1;i<=n;i++)st[0][i]=seq[i]; for(rg int t=1;t<20;t++) for(rg int i=1;i<=n;i++) if(i+bin[t]-1<=n)st[t][i]=dma(st[t-1][i],st[t-1][i+bin[t-1]]); } int sequence_query(rg int x,rg int y) { rg int t=lgg[y-x+1]; return dma(st[t][x],st[t][y-bin[t]+1]); } void jump(rg int x,rg int lim) { for(rg int rig=x,lef=nex[x];lef>=lim;lef=nex[lef]) { while(rig>lef&&sequence_query(lef,rig)>seq[lef])rig=nex[rig]; ans=dma(ans,rig-lef+1); } } int main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); read(n),read(q); for(rg int i=1;i<=n;i++) { read(seq[i]); vc.push_back(seq[i]); } std::sort(vc.begin(),vc.end()); vc.erase(std::unique(vc.begin(),vc.end()),vc.end()); for(rg int i=0;i<vc.size();i++) hc[vc[i]]=i+1; for(rg int i=1;i<=n;i++)seq[i]=hc[seq[i]]; memset(fir,-1,sizeof fir); for(rg int i=1;i<=n;i++) { nex[i]=fir[seq[i]]; fir[seq[i]]=i; } sequence_table(); for(rg int x,y;q;q--) { ans=1; read(x),read(y); for(rg int i=y;i>=x;i--) jump(i,x); printf("%d\n",ans); } fclose(stdin); fclose(stdout); return 0; }
出题人一定是月厨,有爱~
考虑将这些进行jump的操作序列转化成链,离线后,用链来更新询问
对于所有链长度小于s的,用树状数组区间最值+扫描线做
对于所有链长度大于s的,这样的链只有n/s条,对每条链的下标预处理每个下标i所在链上的位置,对每个询问算出最优值并更新,复杂度为n*n/s
取s=sqrt(n/logn),可以得到O(n*sqrt(n*logn))的复杂度
#include <map> #include <vector> #include <math.h> #include <stdio.h> #include <string.h> #include <algorithm> template<class T> T mex(T x,T y) {return x>y?x:y;} template<class T> void read(T &x) { int c=getchar();bool b=0; for(;c<48||c>57;c=getchar())if(c==45)b=1; for(x=0;c>47&&c<58;c=getchar())x=(x<<1)+(x<<3)+c-48; if(b)x=-x; } const int maxn=500010; bool vis[maxn]; int n,m,lim,A[maxn],st[20][maxn],lgg[maxn],L[maxn],R[maxn],ans[maxn],nex[maxn],big[maxn],cur[maxn],tr[maxn]; std::map<int,int> pool; std::vector<int> seq; std::vector<std::pair<int,int> > Q[maxn]; inline void Add(int x,int c) { for(;x<=n;x+=x&-x)tr[x]=mex(tr[x],c); } inline int Get(int x) { int ret=0; for(;x;x-=x&-x)ret=mex(ret,tr[x]); return ret; } inline int maxi(int x,int y) { int t=lgg[++y-x]; return mex(st[t][x],st[t][y-(1<<t)]); } int main() { freopen("sequence.in","r",stdin); freopen("sequence.out","w",stdout); read(n),read(m); lgg[0]=-1;for(int i=1;i<=n;i++) { read(A[i]); st[0][i]=A[i]; lgg[i]=lgg[i>>1]+1; } for(int i=1;i<=m;i++) { read(L[i]),read(R[i]); Q[L[i]].push_back(std::make_pair(R[i],i)); ans[i]=1; } for(int t=1;t<20;t++) for(int i=1;i+(1<<t)-1<=n;i++) st[t][i]=mex(st[t-1][i],st[t-1][i+(1<<t-1)]); for(int i=n,j;i;i--) { if(pool.count(A[i])) { j=pool[A[i]]; if(maxi(i,j)<=A[i])nex[i]=j; } pool[A[i]]=i; } lim=static_cast<int>(sqrt(n/log(n)/log(2))); for(int i=1;i<=n;i++) if(!vis[i]) { seq.clear(); seq.push_back(0); for(int j=i;j;j=nex[j]) { seq.push_back(j); vis[j]=true; } if(seq.size()>=lim) { for(int j=1,k=0;j<=n;j++) { for(;k<seq.size()&&j>=seq[k];k++)big[seq[k]]=1; cur[j]=k-1; } for(int j=1,x,y;j<=m;j++) if(cur[L[j]-1]+1<seq.size()) { x=seq[cur[L[j]-1]+1]; y=seq[cur[R[j]]]; ans[j]=mex(ans[j],y-x+1); } } } for(int i=n;i;i--) { if(!big[i]) for(int j=i;j;j=nex[j]) Add(j,j-i+1); for(int j=0;j<Q[i].size();j++) ans[Q[i][j].second]=mex(ans[Q[i][j].second],Get(Q[i][j].first)); } for(int i=1;i<=m;i++)printf("%d\n",ans[i]); fclose(stdin); fclose(stdout); return 0; }