Make Rounddog Happy【序列分治+DP】

DP維護從某點 往後/往前 遇到的第一對非法點對。

然後分治計數,合併的時候根據最大值是 由左區間貢獻還是右區間貢獻 來進行分類,注意去重。

可以枚舉左/右區間端點,這樣即能固定最大值也能固定剩餘區間的長度。

複雜度O(NlogN)

#include <bits/stdc++.h>
#define rep(i, a, b) for(int i = (a); i <= (b); i++)
#define per(i, a, b) for(int i = (a); i >= (b); i--)
#define pii pair<long long,long long>
#define ll long long
#define pb push_back
#define all(x) (x).begin,(x).end()
const int N=3e5+100;
using namespace std;
int s[N],n,k,dp1[N],nxt[N],pre[N],dp2[N];
ll f(int l,int r) {
    if(l==r) {
        if(s[l]-1<=k) return 1;
        else return 0;
    }
    int mid = (l+r)>>1;
    ll ans = f(l,mid)+f(mid+1,r);

    int now = 0;
    int p = mid+1;
    per(i, mid, l) {
        now = max(now,s[i]);
        int R = min(dp1[i]-1,r);
        while(s[p]<=now&&p<=r) p++;
        R = min(R,p-1);
        int L = mid + now-k-(mid-i+1);
        L = max(L,mid+1);
        if(L<=R) ans += (R-L+1);
    }
    now = 0;
    p = mid;
    rep(i, mid+1, r){
        now = max(now,s[i]);
        int L = max(dp2[i]+1,l);
        while(s[p]<now&&p>=l) p--;
        L = max(L,p+1);
        int R = mid - (now-k-(i-mid)) +1;
        R = min(R,mid);
        if(L<=R) ans += (R-L+1);
    }
    return ans;
}
int main() {
    //freopen("a.txt","r",stdin);
    ios::sync_with_stdio(false);
    int T;
    cin>>T;
    while(T--) {
        cin>>n>>k;
        rep(i, 1, n) cin>>s[i];
        rep(i, 1, 300000) nxt[i] = n+1,pre[i] = 0;
        dp1[n+1] = n+1;
        per(i, n, 1) {
            dp1[i] = min(dp1[i+1],nxt[s[i]]);
            nxt[s[i]] = i;
        }
        dp2[0] = 0;
        rep(i, 1, n) {
            dp2[i] = max(dp2[i-1],pre[s[i]]);
            pre[s[i]] = i;
        }
        cout<<f(1,n)<<endl;
    }
    return 0;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章