1. 程式人生 > 實用技巧 >[2020牛客暑期多校訓練營(第二場)Greater and Greater]

[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

*/