[2020牛客暑期多校训练营(第二场)Greater and Greater]
2020牛客暑期多校训练营(第二场)Greater and Greater
题目大意:
给你一个n大小的序列A和一个m大小的序列B,问A这个序列有多少个m大小的子区间满足(假设子区间为S) 任意 \(1<=i<=m\) ,\(S_i>B_i\)
题解:
这个官方题解讲的很清楚了,但是我并没有看懂。
-
首先考虑 \(n*m\) 的做法。
\(dp[i][j]\) 表示从后面往前匹配,A串匹配到了第 \(i\) 位,B串匹配到了第 \(j\) 位,是否完全匹配。
状态转移方程 \(dp[i][j]=(A_i>=B_j)\&dp[i+1][j+1]\) 这个 $dp[i][j]=0 ,or,1 $
-
然后考虑 \(bitset\) 优化 \(dp\) ,因为每一位只有0或者1,而且 \(dp[i]\) 只与 \(dp[i+1]\) 有关系,所以考虑一起转移,定义一个 \(bitset\) ,\(dp[i]\) 表示A串从后往前匹配到了第 \(i\) 位,因为 \(dp[i]\) 的数据类型是 \(bitset\) ,所以其中第 \(j\) 位如果是1,表示 \(dp[i][j]=1\) 。
-
再考虑 \(A_i>=B_i\) ,因为要一起转移,所以对于每一个 \(A_i\) 都要同时判断和 \(B_j\) 的大小。 这就意味着要求出每一个 \(i\) 和B串所有位置的关系,这个也可以用一个 \(bitset\) 来存。
-
这个存也是有一点点小技巧的,如果我们把 B 串从小到大排个序,那么最多是不是就只能分成 \(m+1\) 份,所以说最多只有 \(m+1\) 种不同的 \(bitset\)
-
先把这个 \(m+1\) 种不同的 \(bitset\) 存下来,然后对于每一个 \(A_i\) 我先二分判断它在排序之后的 \(B\) 串的位置,然后再选择要用哪个 \(bitset\) 。(虽然 \(m*m\) 很大,但是因为 \(bitset\) 只占一个 \(bit\) 所以并没有超空间)
-
最后就是转移方程了,我一直 \(S_i\) (表示 \(A_i\) 的 \(biset\)),已知 \(dp[i+1]\)
转移方程 \(dp[i]=S_i\&(dp[i+1]<<1)\) 这个为什么是 \(dp[i+1]<<1\) 应该很好理解把?
因为 \(dp[i][j]\) 是需要 \(dp[i+1][j+1]\) 来判断的。
但是这样就是对的转移方程了吗?
其实还差一点点,因为对于 \(dp[i][m]\) 是只需要 \(A_i>B_m\) 即可,所以 \(dp[i+1]\) 往前挪一位之后空下来的那一位应该要是1才行,所以 \(dp[i+1]<<1\) 之后还要按位与一个 \(1<<m\) 。
所以最终的转移方程就是:
\(dp[i]=S_i\&((dp[i+1]<<1)|(1<<m))\)
#include <bits/stdc++.h>
#define debug(x) printf("debug:%s=%d\n",#x,x);
//#define debug(x) cout << #x << ": " << x << endl;
using namespace std;
typedef long long ll;
const int maxn = 2e5+10;
const int M = 4e4+10;
bitset<M>dp[2];
bitset<M>S[M];
int a[maxn],v[M],p[M];
bool cmp(int a,int b){
return v[a]<v[b];
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) {
scanf("%d",&v[i]);
p[i]=i;
}
sort(p+1,p+1+m,cmp);
sort(v+1,v+1+m);
for(int i=1;i<=m;i++) {
S[i]=S[i-1],S[i].set(p[i]-1);
}
bitset<M> now;now.reset();now.set(m-1);
bitset<M> base;base.reset();base.set(0);
int ans=0,id=0;
for(int i=n;i>=1;i--){
int t = upper_bound(v+1,v+1+m,a[i])-v-1;
dp[id^1]=S[t]&((dp[id]>>1)|now);
if((dp[id^1]&base).count()) ans++;
id^=1;
dp[id^1]=dp[id];
}
printf("%d\n",ans);
return 0;
}
/*
6 4
8 6 4 5 6 5
7 4 6 3
*/