牛客练习赛10 E 数列查找 [莫队+分块]
2018-01-20 18:32
239 查看
题意:给你长度为n的数列,m次询问,每次询问求【L,R】中,是第k1个次数的第k2个数是多少。
题解:很巧妙的解法,一开始想到的是莫队+线段树,转移时间为O(m*sqrt(m)*logn),得出结果时间为O(m*logn),所以总的时间复杂度为O(m*(sqrt(m)*logn+m*logn))就TLE了,可以看出主要的时间复杂度在转移上,所以我们可以通过减少转移的时间增加查询的时间,使得总的复杂度更低,于是可以采用分块的方法,我们需要四个数组:
所以我们只需要O(1)转移,修改次数总和块的信息、某次数下数的个数的块的信息。然后查询的时候通过O(sqrt(n))查找第k1的次数是多少,与该次数下第k2的数是多少的信息,所以我们总的复杂度为O(m*(sqrt(m)+sqrt(n))就不会TLE了。
AC代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#define N 50005
using namespace std;
int a
;
int real
,pos
;
int num
;//a[i]出现次数
int bcot[205],cot
;//次数类型的总数
int blo
[205];//该次数下数的个数
int len;
struct node
{
int l,r,k1,k2,id;
node(){}
node(int l,int r,int k1,int k2,int id)
{
this->l=r;
this->r=r;
this->k1=k1;
this->k2=k2;
this->id=id;
}
}q
;
int n,m;
int ans
;
bool cmp(node a,node b)
{
return pos[a.l]<pos[b.l]||pos[a.l]==pos[b.l]&&a.r<b.r;
}
int qcot(int need)
{
int sum=0;
for(int i=0;i<=len+1;i++)
{
sum+=bcot[i];
if(sum>=q[need].k1)
{
sum-=bcot[i];
for(int j=i*len;j<(i+1)*len;j++)
{
sum+=cot[j];
if(sum>=q[need].k1)
return j;
}
}
}
return -1;
}
int qnum(int cot,int need)
{
int sum=0;
for(int i=0;i<=len+1;i++)
{
sum+=blo[cot][i];
if(sum>=q[need].k2)
{
sum-=blo[cot][i];
for(int j=i*len;j<(i+1)*len;j++)
{
if(num[j]==cot)sum++;
if(sum>=q[need].k2)
return j;
}
}
}
while(1);
return -1;
}
void change(int number,int flag)
{
if(flag)
{
if(num[number]!=0)
{
blo[num[number]][number/len]--;
if(real[num[number]]==1)bcot[num[number]/len]--,cot[num[number]]--;
real[num[number]]--;
}
blo[++num[number]][number/len]++;
if(real[num[number]]==0)bcot[num[number]/len]++,cot[num[number]]++;
real[num[number]]++;
}
else
{
blo[num[number]][number/len]--;
if(real[num[number]]==1)bcot[num[number]/len]--,cot[num[number]]--;
real[num[number]]--;
if(num[number]!=1)
{
blo[--num[number]][number/len]++;
if(real[num[number]]==0)bcot[num[number]/len]++,cot[num[number]]++;
real[num[number]]++;
}
else num[number]--;
}
}
int main()
{
scanf("%d",&n);
len=(int)sqrt((double)n+0.1);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1;i<=n;i++)
pos[i]=(i-1)/len+1;
for(int i=0;i<m;i++)
{
scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].k1,&q[i].k2);
q[i].id=i;
}
sort(q,q+m,cmp);
int l=1,r=0;
for(int i=0;i<m;i++)
{
if(r<q[i].r)
{
for(r=r+1;r<=q[i].r;r++)
change(a[r],1);
r--;
}
if(q[i].l<l)
{
for(l=l-1;l>=q[i].l;l--)
change(a[l],1);
l++;
}
if(r>q[i].r)
{
for(;r>q[i].r;r--)
change(a[r],0);
}
if(q[i].l>l)
{
for(;l<q[i].l;l++)
change(a[l],0);
}
int now=qcot(i);
now=qnum(now,i);
ans[q[i].id]=now;
}
for(int i=0;i<m;i++)
printf("%d\n",ans[i]);
}
题解:很巧妙的解法,一开始想到的是莫队+线段树,转移时间为O(m*sqrt(m)*logn),得出结果时间为O(m*logn),所以总的时间复杂度为O(m*(sqrt(m)*logn+m*logn))就TLE了,可以看出主要的时间复杂度在转移上,所以我们可以通过减少转移的时间增加查询的时间,使得总的复杂度更低,于是可以采用分块的方法,我们需要四个数组:
int num ;//a[i]出现次数 int bcot[205],cot ;//次数类型的总数 int blo [205];//该次数下数的个数
所以我们只需要O(1)转移,修改次数总和块的信息、某次数下数的个数的块的信息。然后查询的时候通过O(sqrt(n))查找第k1的次数是多少,与该次数下第k2的数是多少的信息,所以我们总的复杂度为O(m*(sqrt(m)+sqrt(n))就不会TLE了。
AC代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#define N 50005
using namespace std;
int a
;
int real
,pos
;
int num
;//a[i]出现次数
int bcot[205],cot
;//次数类型的总数
int blo
[205];//该次数下数的个数
int len;
struct node
{
int l,r,k1,k2,id;
node(){}
node(int l,int r,int k1,int k2,int id)
{
this->l=r;
this->r=r;
this->k1=k1;
this->k2=k2;
this->id=id;
}
}q
;
int n,m;
int ans
;
bool cmp(node a,node b)
{
return pos[a.l]<pos[b.l]||pos[a.l]==pos[b.l]&&a.r<b.r;
}
int qcot(int need)
{
int sum=0;
for(int i=0;i<=len+1;i++)
{
sum+=bcot[i];
if(sum>=q[need].k1)
{
sum-=bcot[i];
for(int j=i*len;j<(i+1)*len;j++)
{
sum+=cot[j];
if(sum>=q[need].k1)
return j;
}
}
}
return -1;
}
int qnum(int cot,int need)
{
int sum=0;
for(int i=0;i<=len+1;i++)
{
sum+=blo[cot][i];
if(sum>=q[need].k2)
{
sum-=blo[cot][i];
for(int j=i*len;j<(i+1)*len;j++)
{
if(num[j]==cot)sum++;
if(sum>=q[need].k2)
return j;
}
}
}
while(1);
return -1;
}
void change(int number,int flag)
{
if(flag)
{
if(num[number]!=0)
{
blo[num[number]][number/len]--;
if(real[num[number]]==1)bcot[num[number]/len]--,cot[num[number]]--;
real[num[number]]--;
}
blo[++num[number]][number/len]++;
if(real[num[number]]==0)bcot[num[number]/len]++,cot[num[number]]++;
real[num[number]]++;
}
else
{
blo[num[number]][number/len]--;
if(real[num[number]]==1)bcot[num[number]/len]--,cot[num[number]]--;
real[num[number]]--;
if(num[number]!=1)
{
blo[--num[number]][number/len]++;
if(real[num[number]]==0)bcot[num[number]/len]++,cot[num[number]]++;
real[num[number]]++;
}
else num[number]--;
}
}
int main()
{
scanf("%d",&n);
len=(int)sqrt((double)n+0.1);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
scanf("%d",&m);
for(int i=1;i<=n;i++)
pos[i]=(i-1)/len+1;
for(int i=0;i<m;i++)
{
scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].k1,&q[i].k2);
q[i].id=i;
}
sort(q,q+m,cmp);
int l=1,r=0;
for(int i=0;i<m;i++)
{
if(r<q[i].r)
{
for(r=r+1;r<=q[i].r;r++)
change(a[r],1);
r--;
}
if(q[i].l<l)
{
for(l=l-1;l>=q[i].l;l--)
change(a[l],1);
l++;
}
if(r>q[i].r)
{
for(;r>q[i].r;r--)
change(a[r],0);
}
if(q[i].l>l)
{
for(;l<q[i].l;l++)
change(a[l],0);
}
int now=qcot(i);
now=qnum(now,i);
ans[q[i].id]=now;
}
for(int i=0;i<m;i++)
printf("%d\n",ans[i]);
}
相关文章推荐
- 牛客练习赛10
- 牛客练习赛10
- 牛客练习赛7 E 珂朵莉的数列 (只有思路)
- 牛客练习赛7E 珂朵莉的数列
- 牛客练习赛10
- 牛客练习赛7 E 题 珂朵莉的数列 【树状数组 + 思维】
- 牛客练习赛7 E 珂朵莉的数列(树状数组+爆long long解决方法)
- 牛客练习赛10 B栈和排序【思维】
- 牛客练习赛10 B-栈和排序
- 大视野在线测评:2038: [2009国家集训队]小Z的袜子(hose) 莫队算法,分块处理
- 牛客练习赛12
- 牛客练习赛12-B-迷宫
- 牛客练习赛6 D 世界上最可爱的珂朵莉
- 编程考试&&noi原题: 1.11编程基础之二分查找10:河中跳房子
- 牛客练习赛6 D 世界上最可爱的珂朵莉 贪心
- 牛客练习赛6 B 点权和 树点权和
- 牛客练习赛3 F 监视任务 每个区间K个 树状数组+贪心
- BZOJ 2038: [2009国家集训队]小Z的袜子(hose)|分块|莫队算法
- 牛客练习赛7 购物 DP 初始化问题
- 有序数列中查找和为某定值的两个数