1. 程式人生 > >【刷題】BZOJ 2096 [Poi2010]Pilots

【刷題】BZOJ 2096 [Poi2010]Pilots

pen AD 結果 pty sam pre -m 字串 兩個

Description

Tz又耍畸形了!!他要當飛行員,他拿到了一個飛行員測試難度序列,他設定了一個難度差的最大值,在序列中他想找到一個最長的子串,任意兩個難度差不會超過他設定的最大值。耍畸形一個人是不行的,於是他找到了你。

Input

輸入:第一行兩個有空格隔開的整數k(0<=k<=2000,000,000),n(1<=n<=3000,000),k代表Tz設定的最大值,n代表難度序列的長度。第二行為n個由空格隔開的整數ai(1<=ai<=2000,000,000),表示難度序列。

Output

輸出:最大的字串長度。

Sample Input

3 9
5 1 3 5 8 6 6 9 10

Sample Output

4
(有兩個子串的長度為4: 5, 8, 6, 6 和8, 6, 6, 9.最長子串的長度就是4)

Solution

枚舉每個點,看以它作為右端點的最優答案是什麽
考慮一個區間,我們其實只需要看最大值和最小值
如果它們的差不滿足要求,那最大值和最小值肯定要走一個,從區間裏踢掉,\(l\) 指針肯定要往右走。假設最大值的位置是 \(i\) ,最小值的位置是 \(j\) ,那麽 \(l\) 指針貪心地走到 \(min(i,j)+1\) 的位置,接著再看區間的最大最小值是否滿足要求
最大值最小值會改變,於是就拿兩個單調隊列維護單調遞增和單調遞減,這樣踢掉一個最值後,可以馬上知道下一個最值
那這個是 \(n^2\)

的,因為每次枚舉的點都要從頭開始考慮
但我們發現,右端新加進來一個點對於左邊不斷踢點是沒有影響的。右端加進來一個點,不可能會使最優區間的 \(l\) 還往左走
所以不需要每個點都從頭考慮,直接一遍掃過去然後不停加點就行了

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=3000000+10;
int n,k,A[MAXN],ans,ps=1
; std::deque<int> q1,q2; template<typename T> inline void read(T &x) { T data=0,w=1; char ch=0; while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar(); if(ch=='-')w=-1,ch=getchar(); while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar(); x=data*w; } template<typename T> inline void write(T x,char ch='\0') { if(x<0)putchar('-'),x=-x; if(x>9)write(x/10); putchar(x%10+'0'); if(ch!='\0')putchar(ch); } template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);} template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);} template<typename T> inline T min(T x,T y){return x<y?x:y;} template<typename T> inline T max(T x,T y){return x>y?x:y;} int main() { read(k);read(n); for(register int i=1;i<=n;++i) { read(A[i]); while(!q1.empty()&&A[i]>=A[q1.back()])q1.pop_back(); while(!q2.empty()&&A[i]<=A[q2.back()])q2.pop_back(); q1.push_back(i);q2.push_back(i); while(A[q1.front()]-A[q2.front()]>k) if(q1.front()<q2.front())ps=q1.front()+1,q1.pop_front(); else ps=q2.front()+1,q2.pop_front(); chkmax(ans,i-ps+1); } write(ans,'\n'); return 0; }

寫了單調隊列後,發現二分+ST表好像也可以做,二分區間的長度, \(O(n)\) 枚舉左端點,用ST表找最值,看它們的差是否滿足要求
然後寫了一下,交一發,結果,卡空間?洛谷RE,BZOJ直接TLE(而且還是只測了1s不到,就直接變成30sTLE,什麽操作?)
所以這個東東就當好玩吧,也沒去調細節了

#include<bits/stdc++.h>
#define ui unsigned int
#define ll long long
#define db double
#define ld long double
#define ull unsigned long long
const int MAXN=3000000+10;
int n,k,A[MAXN],g[MAXN],Mx[MAXN][24],Mn[MAXN][24];
template<typename T> inline void read(T &x)
{
    T data=0,w=1;
    char ch=0;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    x=data*w;
}
template<typename T> inline void write(T x,char ch='\0')
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
    if(ch!='\0')putchar(ch);
}
template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
template<typename T> inline T min(T x,T y){return x<y?x:y;}
template<typename T> inline T max(T x,T y){return x>y?x:y;}
inline void init()
{
    for(register int i=1;i<=n;++i)g[i]=(int)log2(i);
    for(register int j=1;j<=22;++j)
        for(register int i=1;i+(1<<j)-1<=n;++i)
        {
            Mx[i][j]=max(Mx[i][j-1],Mx[i+(1<<j-1)][j-1]);
            Mn[i][j]=min(Mn[i][j-1],Mn[i+(1<<j-1)][j-1]);
        }
}
inline int calc(int l,int len)
{
    int r=l+len-1,k=g[len];
    return max(Mx[l][k],Mx[r-(1<<k)+1][k])-min(Mn[l][k],Mn[r-(1<<k)+1][k]);
}
inline bool check(int len)
{
    for(register int i=1;i+len-1<=n;++i)
        if(calc(i,len)<=k)return true;
    return false;
}
int main()
{
    read(k);read(n);
    for(register int i=1;i<=n;++i)
    {
        read(A[i]);
        Mx[i][0]=Mn[i][0]=A[i];
    }
    init();
    int l=1,r=n,ans=1;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid))ans=mid,l=mid+1;
        else r=mid-1;
    }
    write(ans,'\n');
    return 0;
}

【刷題】BZOJ 2096 [Poi2010]Pilots