2020牛客暑期多校训练营(第二场)G. Greater and Greater

https://ac.nowcoder.com/acm/contest/5667/G

题目描述:给长为n的数组a,和长为m的数组b。问a中有多少个长为m的连续的子数组s满足:s[i]>=b[i],i∈[1,m]。

 

 官方题解如上,接下来结合我的理解解释一下:

题解的s数组不难理解,s[i][j]=1就表示a[i]>=b[j],j∈[1,m]。s[i]就是对于a[i]来说,是否满足a[i]这个数>=b数组中的数。

假设把b数组排序后,对一个数a[i],a[i]按大小插入b数组,那么a[i]前面的都小于a[i],也就是把对应在s[i]的位置设为1,a[i]后面的数都大于a[i],所以设为0。因此,s[i]只有m种,取决于a[i]所在排序后b数组的位置。

而cur数组是设置的最为巧妙地,

对cur[i]来说,把他地关系列出来:

cur[i][m]=      a[i]>=b[m]

cur[i][m-1]=     (a[i]>=b[m-1]) && (a[i+1]>=b[m])

cur[i][m-2]=     (a[i]>=b[m-2]) && (a[i+1]>=b[m-1]) && (a[i+2]>=b[m])

.......依次下去

可以看出cur[i][j]就是      a[i]、a[i+1]...a[i+m-j]分别>=b[j]、b[j+1]...b[m]。

也就是从a[i]~a[i+m-j] 与b个若干个后缀b[j]~b[m]一一匹配的关系。
那么很明显,当cur[i][1]=1是,也就是满足了a[i]~a[i+m-1]都分别对应>=b[1]~b[m]。也就是答案要求的子数组。
我们只要考虑能线性求出cur[i]的方法,遍历一遍统计答案。
观察cur[i+1]:

cur[i+1][m]=      a[i+1]>=b[m]

cur[i+1][m-1]=     (a[i+1]>=b[m-1]) && (a[i+2]>=b[m])

和上面的cur[i]放在一起:

cur[i][m-1]=     (a[i]>=b[m-1]) && (a[i+1]>=b[m])

cur[i][m-2]=     (a[i]>=b[m-2]) && (a[i+1]>=b[m-1]) && (a[i+2]>=b[m])

很容易就想到cur[i]=cur[i+1]移位,在和a[i]对应的s数组做&运算。

注意bitset是从高位开始存储:m、m-1......1。

所以cur[i]=((cur[i+1]>>1)|Im)& s[i]。

代码:

#include<cstdio>
#include<iostream>
#include<deque>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#include<stack>
#include<algorithm>
#include<queue>
#include<set>
#include<bits/stdc++.h>
#define cfast ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define sd(x) scanf("%d",&x)
#define lsd(x) scanf("%lld",&x)
#define sf(x) scanf("%lf",&x)
#define ms(x,y) memset(x,y,sizeof x)
#define fu(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define all(a) a.begin(),a.end()
#define range(a,x,y) a+x,a+y+x
#define dbug printf("bbbk\n")
using namespace std;
using namespace __gnu_cxx;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<ll,ll> P;
const int N=2e5+9;
const int MN=4e4+9;
const ll mod=998244353;
const int MAX=1e9;
const ll base=1331;
const ll INF=1e15+7;
int a[N],b[N],pos[N];
bitset<MN> s[MN],cur,I;
bool cmp(int x,int y){
    return b[x]<b[y];
}
int main()
{
    //freopen("t.txt","r",stdin);
    cfast;
    int n,m;cin>>n>>m;
    fu(i,1,n) cin>>a[i];
    fu(i,1,m) cin>>b[i],pos[i]=i;
    sort(pos+1,pos+m+1,cmp);
    fu(i,1,m) s[i]=s[i-1],s[i][pos[i]]=1;
    I[m]=1;
    int ans=0;
    sort(b+1,b+m+1);
    //bitset<4> ss;ss[3]=1;
    //cout<<"tmp:  "<<ss<<"   "<<(ss<<1)<<endl;
    fd(i,n,1){
        int p=upper_bound(b+1,b+m+1,a[i])-b-1;//找到在>=b中的最小位置
        //bitset是从高位存储,m、m-1、m-2......1
        //cur[i][j]表示从a[i]开始,
        //a[i]、a[i+1]...a[i+m-j]分别>=b[j]、b[j+1]...b[m]。
        //也就是从i开始的后缀一一匹配
        //再观察cur[i]和cur[i+1]的关系,就不难得出下式
        cur=(((cur>>1)|I)&s[p]);
        if(cur[1]==1) ans++;
    }
    cout<<ans<<endl;
    return 0;
}

 

posted on 2021-07-14 11:13  Aminers  阅读(38)  评论(0编辑  收藏  举报

导航