【刷題】BZOJ 2096 [Poi2010]Pilots
阿新 • • 發佈:2018-05-02
pen AD 結果 pty sam pre -m 字串 兩個
的,因為每次枚舉的點都要從頭開始考慮
但我們發現,右端新加進來一個點對於左邊不斷踢點是沒有影響的。右端加進來一個點,不可能會使最優區間的 \(l\) 還往左走
所以不需要每個點都從頭考慮,直接一遍掃過去然後不停加點就行了
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