【Codeforces 1167E】Range Deleting

原題鏈接:https://codeforces.ml/problemset/problem/1167/E

題目大意:

給出數組a,a_i<=m,長度爲n

定義操作f(l,r):將數組中 l<=a_i<=r刪除後,得到的新數組。

問有多少對{l,r},使得執行f(l,r)操作後,剩下的序列爲單調不遞減序列

例如:2 3 1 刪掉2 3 後 變爲 1 ,此時單調不遞減,所以f(2,3)是可行操作f(2,3)

題目思路:

卡了一下午的題目叭..

這個題目確實有點驚訝到我(或許是太菜,被輕易驚訝、

首先,可以確定,如果刪除一段區間[l,r]內所有的數之後,新數組單調不遞減,在這基礎上如果刪掉[x,y] x<=l && y>=r 此時新數組絕對還是單調不遞減,由此可見:刪除操作具有單調性。

瞭解單調之後,考慮求區間個數採取尺取 或者 二分

之後便有了統一套路,可以不可以求出以l開頭的所有合法區間?

所以只需要找到右邊第一個合法的,那麼向右擴充的區間全部合法

假設要刪除的區間爲[l,r] ,如果刪除l,r區間後合法

那麼需要滿足[1,l-1]是單調不遞減的的,[r+1,m]也是不遞減的,並且滿足 [1,l-1]所有的數 都在 [r+1,n]的左邊即可

那麼[l,r]即爲合法區間

所以:

1.需要處理 1,pref 是否可行的最遠的pref,當l-1大於pref時即不可行

2.需要處理 curf,n 是否可行的最近的curf,當r+1小於curf時即不可行

3.0 一定可行  n+1一定可行

4.之後我們預處理出 每個a_i 最左邊出現的位置,與最右邊出現的位置,當且僅當R[l-1]<L[r+1]時 區間可行

5.考慮到一些未出現的數字,5未出現但是還需要和6 作比較,那麼取小於5的第一個數作比較,這裏使用並查集進行優化。

6.最後套一下尺取模板,解決了問題:

/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const ll INF=1e17;
const int maxn=2e6+6;
const int mod=1e9+7;
const double eps=1e-9;
const double PI = acos(-1);
inline bool read(ll &num)
{char in;bool IsN=false;
in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
int L[maxn],R[maxn];
int vis[maxn];
ll num[maxn];
int ls=0,rs=m+1;
int pre[maxn],cur[maxn];
int Find_pre(int x){
    if(!x) return x;
    if(!vis[x]) return pre[x]=Find_pre(pre[x]);
    return x;
}
int Find_cur(int x){
    if(x==m+1) return x;
    if(!vis[x]) return cur[x]=Find_cur(cur[x]);
    return x;
}
bool check(int l,int r){
    if(l-1<=ls&&r+1>=rs){
        int dx=Find_pre(l-1);
        int dy=Find_cur(r+1);
        if(R[dx]<L[dy]) return true;
    }
    return false;
}
int main(){
    read(n);read(m);
    for(int i=1;i<=m;i++) cur[i]=i+1,pre[i]=i-1;
    R[0]=0;
    L[m+1]=n+5;
    for(int i=1;i<=n;i++){
        read(num[i]);
        if(!L[num[i]]) L[num[i]]=i;
        R[num[i]]=i;
        vis[num[i]]=1;
    }
    int lst=0;
    for(int i=1;i<=m;i++){
        if(!L[i]){
            ls=i;
            continue;
        }
        if(L[i]<lst) break;
        ls=i;
        lst=R[i];
    }
    lst=n+5;
    for(int i=m;i>=1;i--){
        if(!L[i]){
            rs=i;
            continue;
        }
        if(R[i]>lst) break;
        rs=i;
        lst=L[i];
    }
    ll ans=0;
    int s=1;
    for(int i=1;i<=m;i++){
        while(s<=i&&check(s,i)){
            ans+=m-i+1;
            s++;
        }
    }
    printf("%lld\n",ans);
    return 0;
}
/**
###
**/

總結:

1.這個題的思路確實很妙,我一直卡就卡在怎樣判斷l,r區間是否可行,沒想出O1的辦法。換個思路:從[l,r]刪除後的結果來反推,預處理即可O1判斷區間可行

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