题解 SP1684 【FREQUENT - Frequent values】
沉MO_Official · · 题解
题目:
SP1684
题目难度:
普及+/提高-
孪生题目编号:
UVA11235
部分分写法:
所以部分分写法是朴素算法
极端朴素,暴力枚举统计什么的我也不太清楚毕竟上来就是冲着正解去的
正解:
算法:
对于这个给定序列,我们可以这样处理(以样例为例):
这个应该很好做,一个
然后,对于每个区间
实际代码实现中,
该区间中的答案有
-
[L,l]$ 中元素个数 $presum_l-L+1 -
(l,r]$ 中的区块做 $\text{RMQ}$ ,找个数最大的。 $max(f_{l+1,s},f_{r-(1<<s)+1,s}) -
(r,R]$ 中元素个数 $R-presum_r
以上
注意一下
做完了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int LOG=17;
int tot,n,q,a[100005],cnt[100005],presum[100005],Log[100005],f[100005][LOG+1],L,R;
int findL()//二分找l
{
int Ll=1,Rr=tot,ans=0;
while(Ll<=Rr)
{
int mid=(Ll+Rr)/2;
if(presum[mid]<L)
ans=mid,Ll=mid+1;
else
Rr=mid-1;
}
// printf("%d ",ans);
return ans+1; //注意要+1,因为找到的是l的左边
}
int findR()//二分找r
{
int Ll=1,Rr=tot,ans=0;
while(Ll<=Rr)
{
int mid=(Ll+Rr)/2;
if(presum[mid]<R)
ans=mid,Ll=mid+1;
else
Rr=mid-1;
}
// printf("%d ",ans);
return ans;
}
int main()
{
cin>>n;
while(n!=0)//注意是多组数据!
{
tot=q=0;
memset(a,0,sizeof(a));
memset(cnt,0,sizeof(cnt));
memset(f,0,sizeof(f));
memset(presum,0,sizeof(presum));
scanf("%d",&q);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(a[i]==a[i-1])
cnt[tot]++;
else
cnt[++tot]++;//cnt的实现
}
Log[0]=-1;
for(int i=1;i<=tot;i++)
f[i][0]=cnt[i],Log[i]=Log[i>>1]+1,presum[i]=presum[i-1]+cnt[i];//cout<<cnt[i]<<" ";
//presum的实现
//cout<<endl;
for(int j=1;j<=LOG;j++)
for(int i=1;i+(1<<j)-1<=tot;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);//RMQ
for(int i=1;i<=q;i++)
{
scanf("%d%d",&L,&R);
int l=findL(),r=findR();
if(l==r)
{
printf("%d\n",max(presum[l]-L+1,R-presum[r]));//特判1,注意个数是否要+-1,可以自己去画图思考。
continue;
}
if(r<l)//特判2
{
printf("%d\n",R-L+1);
continue;
}
int s=Log[r-l];
//printf("%d %d %d\n",max(f[l+1][s],f[r-(1<<s)+1][s]),max(presum[l]-L+1,R-presum[r]),max(max(f[l+1][s],f[r-(1<<s)+1][s]),max(presum[l]-L+1,R-presum[r])));
printf("%d\n",max(max(f[l+1][s],f[r-(1<<s)+1][s]),max(presum[l]-L+1,R-presum[r])));//注意是f[l+1]因为不含第l项
}
cin>>n;
}
return 0;
}