bzoj4358: permu
终于又可以好好学习了?雾zzzz
开始写了一发KDT,做法就是把询问看成二维坐标点,然后按大小顺序插入每一个数字,包含它的区间计数器+1,否则归零
那么答案就是历史最大值
看起来很好写??(雾)
这个东西的标记。。。。呵呵呵。。。
Rose巨神告诉我要用到历史最值线段树相关打标记的方法。。。。orz了吉司机很久然而还没有写出来。。。
换个做法。。也是带根号的莫队
对于每个位置维护在值域中向左右的最大连续长度,每次加入一个数,通过它值域左右的数可以得到更新,然后它不需要把包含它的那连续一整段全部更新,只需要更新这一段的边界点即可
先将左端点分块,一个块中的询问按右端点排序,对于非当前块的数,因为右端点递增相当于扫一次,当前块的每次暴力算一次,然后清空即可
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> using namespace std; int c[51000]; int block,st[51000]; struct query{int l,r,id;}q[51000];int as[51000]; bool cmp(query q1,query q2){return st[q1.l]==st[q2.l]?q1.r<q2.r:st[q1.l]<st[q2.l];} int L[51000],R[51000]; int top,Lid[51000],Rid[51000],Ld[51000],Rd[51000]; void push(int ll,int rr) { top++; Lid[top]=ll,Ld[top]=L[ll]; Rid[top]=rr,Rd[top]=R[rr]; } void pop() { while(top>0) { L[Lid[top]]=Ld[top]; R[Rid[top]]=Rd[top]; top--; } } int main() { freopen("a.in","r",stdin); freopen("a.out","w",stdout); int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&c[i]); for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i; block=int(sqrt(double(n+1)))+1; for(int i=1;i<=n;i++)st[i]=(i-1)/block+1; sort(q+1,q+m+1,cmp); int las=1; for(int b=1;b<=block;b++) { int mmax=0,r=b*block; memset(L,0,sizeof(L)); memset(R,0,sizeof(R)); for(int i=las;st[q[i].l]==b&&i<=m;i++,las=i) { while(q[i].r>r) { r++; L[c[r]]=L[c[r]-1]+1; R[c[r]]=R[c[r]+1]+1; int d=L[c[r]]+R[c[r]]-1; L[c[r]+R[c[r]]-1]=d; R[c[r]-L[c[r]]+1]=d; mmax=max(mmax,d); } as[q[i].id]=mmax; int li=min(q[i].r,b*block); for(int j=q[i].l;j<=li;j++) { L[c[j]]=L[c[j]-1]+1; R[c[j]]=R[c[j]+1]+1; int d=L[c[j]]+R[c[j]]-1; int ll=c[j]+R[c[j]]-1,rr=c[j]-L[c[j]]+1; push(ll,rr); L[ll]=d; R[rr]=d; as[q[i].id]=max(as[q[i].id],d); } pop(); for(int j=q[i].l;j<=li;j++)L[c[j]]=R[c[j]]=0; } } for(int i=1;i<=m;i++)printf("%d\n",as[i]); return 0; }
pain and happy in the cruel world.