Codeforces 6E

题目大意:给定一个长度为n的序列和一个数k,求最大数与最小数的差小于等于k的子序列的最长长度,并输出有多少个,以及各自的左端点和右端点。

不得不说这道题目真是经典。刚开始看到这道题的时候,就觉得要用线段树做了,但始终不知道该怎么搞,我当时的想法很暴力,枚举l,r用线段树暴力判断是否满足,显然T得很惨。看题解有一句关键的话:“若[l,r]满足条件,则[l,r-1]也必然满足条件”。这句话说明了单调性,于是我便自然而然地想到了枚举l,二分查找一个最靠右的r。二分的框架出来了我们如何判断一个区间是否成立呢?如果使用线段树,总时间复杂度是O(nlog^2n)的,效率很低,但是800ms可过。还有一种就是Sparse-Table算法,也就是RMQ问题,用nlogn的时间预处理后O(1)查询,这样复杂度就优化到了O(nlogn)。注意预处理时n一定要倒着枚举!

%%%__debug大神。

#include<cstdio>
#include<utility>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#define x first
#define y second
const int MAXN=100000+10;
typedef std::pair<int,int> pii;
int w[MAXN],max[MAXN][20],min[MAXN][20],n,k,cnt=0,lenans=0;
std::vector<pii> ans;
void Pre()
{
    for(int i=n;i>=1;i--)
    {
        for(int j=1;i+(1<<j)-1<=n;j++)
        {
            max[i][j]=std::max(max[i][j-1],max[i+(1<<(j-1))][j-1]);
            min[i][j]=std::min(min[i][j-1],min[i+(1<<(j-1))][j-1]);
        }
    }
}
int findMaxMin(int l,int r)
{
    int len=r-l+1,k=0,x=1;
    while(x<=len)
    {
        k++;
        x<<=1;
    }k--;
    return std::max(max[l][k],max[r-(1<<k)+1][k])-std::min(min[l][k],min[r-(1<<k)+1][k]);
}
int main()
{
    memset(min,0x3f3f3f3f,sizeof(min));
    scanf("%d %d",&n,&k);
    for(int i=1;i<=n;i++)
        scanf("%d",&w[i]),max[i][0]=min[i][0]=w[i];
    if(n==1)
    {
        printf("1 1\n%d %d\n",w[1],w[1]);
        return 0;
    }
    Pre();
    for(int i=1;i<=n;i++)
    {
        int l=i,r=n;
        //if(l==r)continue;
        int flag=findMaxMin(i,r);
        if(flag<=k&&flag>=0){l=r;goto loop;}
        while(l+1!=r)
        {
            int mid=(l+r)>>1;
            if(findMaxMin(i,mid)<=k)
                l=mid;
            else 
                r=mid;
        }
loop:   if(lenans<l-i+1)
        {
            lenans=l-i+1;
            ans.clear();
            ans.push_back(std::make_pair(i,l));
        }
        else if(lenans==l-i+1)
            ans.push_back(std::make_pair(i,l));
    }
    printf("%d %d\n",lenans,ans.size());
    for(int i=0;i<ans.size();i++)
        printf("%d %d\n",ans[i].x,ans[i].y);
}


你可能感兴趣的:(codeforces)