题目大意
给定一个长n的数列A和一个长m的数列B,要求计算有多少个A中长为m的子串C,每个C[i]分别>=B[i]。
以样例为例:
1 4 2 8 5 72 3 3
这里就有2,8,5和8,5,7符合条件。
解题思路
先附上出题人的写法严谨的题解,由于部分数学表述,所以可读性较差。(不应该自动打我的水印)
正片:
这题需要用到bitset数据结构进行优化,因为我们只需要在后面的dp中用到0或1两种状态,而且bitset可以进行各种二进制运算,节省时间复杂度。
先求出长为m的s[i](s和ans均为bitset),s[i][j]表示了是否A[i]>=B[j],所以不同的s[i]有m种。求出本质不同的那m种情况的bitset,在需要用F[i]时,就可以直接二分了。
在i满足A[i+j-1]>=B[j]时,会出现F[i][1]=1,F[i+1][2]=1,F[i+2][3]=1等等,因此设长度为m的ans,每次ans右移一位都与上A[i]。
此时ans[0]为F[i][1]&F[i+1][2]&F[i+2][3]……。若ans[0]的结果为1,则说明i可行。
AC代码(本人)
这种简化方法是我参考另外一位大佬完成,思路大致相同。
#include<bits/stdc++.h>
using namespace std;
struct hh
{
int x,y;
} a[150010],b[150010];
bitset<150010> t,ans;
int n,m,i,j=1;
bool cmp(hh x,hh y)
{
return x.x>y.x;
}
int main()
{
ans.set();
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%d",&a[i].x);
a[i].y=i;
}
for(i=1;i<=m;i++)
{
scanf("%d",&b[i].x);
b[i].y=i;
}
sort(a+1,a+n+1,cmp);
sort(b+1,b+n+1,cmp);
for(i=1;i<=m;i++)
{
while(j<=n && a[j].x>=b[i].x)
{
t.set(a[j].y);
j++;
}
ans&=t>>(b[i].y-1);
}
printf("%d\n",ans.count());
return 0;
} 正常写法
当然还有我队友按照这一套写的代码(转载自https://ac.nowcoder.com/acm/contest/view-submission?submissionId=44250008)
#include<bits/stdc++.h>
using namespace std;
const int N=150010,M=40010;
bitset<M> a,b[N];
int n,m,ans,c[N],d[N],e[N];
int cmp(int x,int y){return d[x]<d[y];}
int main()
{
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++) scanf("%d",&c[i]);
for(int i=0;i<m;i++) scanf("%d",&d[i]),e[i]=i;
sort(e,e+m,cmp);
for(int i=1;i<=m;i++) b[i]=b[i-1],b[i].set(e[i-1]);
for (int i=n-1;i>=0;i--)
{
a>>=1;int l=0,r=m;
while(l<r)
{
int mid=l+r>>1;
if(c[i]<d[e[mid]]) r=mid;
else l=mid+1;
}
a&=b[l];
if(c[i]>=d[m-1]) a.set(m-1);
ans+=a[0];
}
cout<<ans<<endl;
return 0;
} 
京公网安备 11010502036488号