wannafly 练习赛10 E 数列查找(莫队+分块分块分块......)
链接:https://www.nowcoder.net/acm/contest/58/E
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
给你一个长为n的序列a,m次查询区间[l,r]内出现次数第k1小的数中值第k2小的数是多少?
保证输入合法
保证输入合法
输入描述:
第一行一个数n
第二行n个数表示序列a
第三行一个数m
之后m行每行四个数表示l r k1 k2
输出描述:
对于每次询问输出一行一个数表示答案
示例1
输入
10 3 6 6 8 3 10 1 6 5 6 10 4 7 1 2 5 7 1 1 5 6 1 2 2 6 2 1 8 9 1 1 6 9 1 2 1 2 1 1 1 4 2 1 5 7 1 3 2 6 1 3
输出
3 1 10 6 5 5 3 6 10 10
说明
3 6 6 8 3 10 1 6 5 6
[4,7]中出现1次的有1,3,8,10,第2小的是3
[5,7]中出现1次的有1,3,10,第1小的是1
[5,6]中出现1次的有3,10,第2小的是10
[2,6]中出现2次的有6,第1小的是6
[8,9]中出现1次的有5,6,第1小的是5
[6,9]中出现1次的有1,5,6,10,第2小的是5
[1,2]中出现1次的有3,6,第1小的是3
[1,4]中出现2次的有6,第1小的是6
[5,7]中出现1次的有1,3,10,第3小的是10
[2,6]中出现1次的有3,8,10,第3小的是10
备注:
对于100%的数据,
有1<=n,a[i],m<=40000
数据保证一定能找到那个数
这一题没有码出来,去看了一下别人提交的代码看懂就没自己写了
/////////////////////////////////////////////////
可以考虑进行高维离散化
比如说,对于一个数x,其在序列中出现了y次
开个vector < int > v[ MAXN ]
在v[1] , v[2] ... v[y]中都push_back( x )
然后对于每个vector,分别进行离散化
这样就保证了空间线性
在高维离散化的基础上进行值域分块,然后跑莫队即可
O( nsqrtm + msqrtn ) = O( msqrtn )
/////////////////////////////////////////////////
题解里是高维离散化,但是我看到的代码我感觉那位大佬写的好像更好一点,
他是用了两次分块,次数分块和值分块
#include<cstdio> #include<algorithm> #include<cmath> using namespace std; int read(){ int x=0,f=1;char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();} while ('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); return x*f; } const int N=400005; int n,m,have[N],a[N],ap[N],cnt[N][700],cntk[700],ans[N],pos,bb,blo,L,R; //have[i]表示是否有次数为i的值出现 //ap[i]表示值为i的数字出现了几次 //cntk[i]表示有多少个不同数值出现了k次,k处在i块里 //cnt[i][j]表示出现了i次的值里,值处在j块有多少 struct node{ int l,r,blo,id,k1,k2; }q[N]; bool operator < (const node &A,const node &B){ return A.blo<B.blo||A.blo==B.blo&&A.r<B.r; } void work(int x,int w){ int val=a[x],cc=ap[val]; if (!--have[cc]) cntk[(cc-1)/bb+1]--; cnt[cc][(val-1)/bb+1]--; ap[val]+=w;cc=ap[val]; if (++have[cc]==1) cntk[(cc-1)/bb+1]++; cnt[cc][(val-1)/bb+1]++; //[次数][值所属的块] } int qry(int k1,int k2){ int i=1,j; while (k1>cntk[i]) k1-=cntk[i],i++;//sqrt(n),次数所属的块,每个块有多少个出现了 for (j=(i-1)*bb+1;j<=i*bb;j++)//sqrt(n) if (have[j]) if (!--k1) break; pos=j;i=1;//k1小的是j次/pos次 while (k2>cnt[pos][i]) k2-=cnt[pos][i],i++;//sqrt(n) for (j=(i-1)*bb+1;j<=i*bb;j++)//sqrt(n) if (ap[j]==pos) if (!--k2) break; return j; } int main(){ n=read();bb=(int)sqrt(n); for (int i=1;i<=n;i++) a[i]=read(); m=read();blo=(int)sqrt(m); for (int i=1;i<=m;i++) q[i].l=read(),q[i].r=read(),q[i].k1=read(), q[i].k2=read(),q[i].id=i,q[i].blo=(q[i].l-1)/blo+1; sort(q+1,q+m+1); L=1;R=0; for (int i=1;i<=m;i++) { while (L>q[i].l) work(--L,1); while (R<q[i].r) work(++R,1); while (L<q[i].l) work(L++,-1); while (R>q[i].r) work(R--,-1); ans[q[i].id]=qry(q[i].k1,q[i].k2); } for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
引用了别的大佬的代码,加了一点注释
分块自己用的还是很生涩,惨
假如是高维离散化的话,应该得跟f题一样用链表存一下出现的次数,然后每个次数里面的值再分块查询,emm就这样