CodeForces - 1437E Make It Increasing(确定首尾的最长不下降子序列)
題目鏈接:點擊查看
題目大意:給出一個長度為 n 的序列,現在有 m 個位置被鎖定,也就是無法進行操作,每次操作可以選擇一個沒有被鎖定的位置,將其更改為任意數值,現在問最少進行多少次操作,可以使得整個序列變得嚴格遞增
題目分析:首先對于初始的序列 a[ i ] 將其偏移一下,也就是令 a[ i ] = a[ i ] - i ,目的就是為了查看兩個數是否沖突
舉個很簡單的例子,設 i < j ,如果 a[ i?] = x,a[ j?] = y,如果想要滿足在 a[ i ] 和 a[ j ] 不變的情況下使得 [ i , j ] 這段區間內的數嚴格遞增,顯然需要滿足的一個不等關系就是:a[ j ] - a[ i ] >=?j - i,因為區間 [ i , j ] 之間,至少需要滿足 { a[ i ] , a[ i ] + 1 , ... , a[ i?] + ( j - i ) } 才行,偏移之后 a[ i?] = x - i,a[ j?] = y - j,此時只需要滿足 a[ i ] <= a[ j ] 即可
然后就是判斷是否有解的情況,根據上一段的描述,O( n ) 掃一遍被鎖定的數的位置就好了
考慮 m == 0,也就是沒有數字被鎖定的情況,這種情況可以參考 hdu 5256,答案就是上述偏移過的序列 a 求解一下最長不下降子序列,然后答案就是 n - ans 了,感性理解一下,就是最長不下降子序列中的數字不需要修改,其余位置的數字都需要修改
如果有數字被鎖定了該怎么辦呢?需要看出的一點是,整個序列被 m 個鎖定的位置分成了 m + 1 段,而這 m + 1 段互不干擾,也就是相互獨立,所以我們可以對于每一段單獨求解最長不下降子序列,然后計算答案
唯一不太相同的就是,相鄰兩個被鎖定的位置,會限制這個最長不下降子序列的首尾,換句話說就將問題轉換為了:求解確定首尾的最長不下降子序列,具體實現就是,求解 [ l , r ] 且 a[ l ] 和 a[ r ] 必須包含在內:先令 d[ 1 ] = a[ l ] ,且 d[ 1 ] 不允許修改,按照正常的流程計算完 [ l + 1 , r ] 這段區間的 lis,然后找到小于等于 a[ r ] 的最大位置,此時的長度就是答案了
具體實現可以參考代碼,因為 lis 是二分求解的,時間復雜度均攤下來就是 O( nlogn )
需要注意的細節就是,可以預處理時在 a 數組和 b 數組的首尾加上哨兵節點,這樣就不用特判了
代碼:
//#pragma GCC optimize(2) //#pragma GCC optimize("Ofast","inline","-ffast-math") //#pragma GCC target("avx,sse2,sse3,sse4,mmx") #include<iostream> #include<cstdio> #include<string> #include<ctime> #include<cmath> #include<cstring> #include<algorithm> #include<stack> #include<climits> #include<queue> #include<map> #include<set> #include<sstream> #include<cassert> #include<bitset> using namespace std;typedef long long LL;typedef unsigned long long ull;const int inf=0x3f3f3f3f;const int N=5e5+100;int a[N],b[N],d[N],n,m;bool check() {for(int i=2;i<=m;i++)if(a[b[i]]<a[b[i-1]])return false;return true; }int solve(int l,int r) {int len=1;d[len]=a[l];for(int i=l+1;i<=r;i++){if(a[i]>=d[len])d[++len]=a[i];else{int j=upper_bound(d+1,d+1+len,a[i])-d;if(j!=1)//注意這里,第一個位置不允許修改d[j]=a[i];}}int pos=upper_bound(d+1,d+1+len,a[r])-d-1;return (r-l+1)-pos; }int main() { #ifndef ONLINE_JUDGE // freopen("data.in.txt","r",stdin); // freopen("data.out.txt","w",stdout); #endif // ios::sync_with_stdio(false);scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",a+i);a[i]-=i;}a[0]=-inf,a[n+1]=inf;b[0]=0,b[m+1]=n+1;for(int i=1;i<=m;i++)scanf("%d",b+i);if(!check())return 0*puts("-1");int ans=0;for(int i=1;i<=m+1;i++)ans+=solve(b[i-1],b[i]);printf("%d\n",ans);return 0; }?
總結
以上是生活随笔為你收集整理的CodeForces - 1437E Make It Increasing(确定首尾的最长不下降子序列)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CodeForces - 1437G D
- 下一篇: 牛客 - 牛半仙的妹子图(并查集+bit