题目大意
给定一个长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; }