Time Limits: 1000 ms Memory Limits: 262144 KB Detailed Limits
Description
给定整数 n 和 x,以及一个大小为 n 的序列 a。
你可以选择一个区间 [l,r],然后令 a[i]+=d(l<=i<=r),其中 d 满足 |d|<=x。
要求最大化 a 的最长上升子序列的长度,并输出该值
Input
第一行输入包含两个整数 n 和 x。
接下来一行 n 个整数表示序列 a。
Output
输出只有一行,包含一个整数,a 的最长上升子序列的最大值。
Sample Input
8 10 7 3 5 12 2 7 3 4
Sample Output
5
Data Constraint
对于 5% 的数据点,n,x<=10;
对于另外 10% 的数据点,n,x<=50;
对于另外 13% 的数据点,n<=1000;
对于另外 10% 的数据点,x=0;
对于另外 20% 的数据点,x<=5,n<=50000;
对于另外 17% 的数据点,x=10^9;
对于 100% 的数据点,n<=200000,x<=10^9。
Source / Author: CEOI2018 day1 glo
题解:
一个显然的结论 , 加值肯定在后缀加,减值肯定在前缀减是最优的。
还有 , 加上一个值就要加的透彻 , 直接加x就行了,减同理。
又发现 , 前缀减 , 等价于后缀加。
所以我们只需枚举分界点i , 让后面的加x看看LIS为多少。
我们先倒着做求出g[i]表示以n~i最长下降子序列长度。
然后求f[i]。
f[i]表示i为分界点(从i开始加x)的答案。
最后ans = max(ans , f[i] + g[i] - 1)
为什么是对?因为f[i]表示i为分界点(从i开始加x)的答案 , 那么对于n~i的每个位置都加了个x , 加完之后g[i]大小还是不变的。
减去一是因为i位衔接起来选了两次。
f的求法根平常一样 , 只不过f[i]!=lower_bound(d+1 , d+1+len+1 , a[i])-d , 而是为 lower_bound(d+1 , d+1+len+1 , a[i]+x)-d。
#include
#include
#include
#include
#define mem(a,b) memset(a,b,sizeof(a))
#define mcy(a,b) memcpy(a,b,sizeof(a))
#define N 200010
#define ll long long
#define re register
#define inf 2147483647
#define mod (ll)(1e9+7)
#define open(x) freopen(x".in","r",stdin);freopen(x".out","w",stdout)
using namespace std;
template
T in(T &x)
{
char ch(0);T f=0; x=0;
while(ch < '0' || ch > '9') ch = getchar() , f |= ch == '-';
while(ch>='0' && ch<='9') x = (x<<1) + (x<<3) + ch - '0' , ch = getchar();
return f ? (x = -x) : x;
}
int n,x;
int a[N], len,d[N],ans,f[N],g[N];
int main()
{
open("glo");
in(n) , in(x);
for(int i=1;i<=n;i++) in(a[i]);
for(int i=1;i<=n;i++)
{
d[len+1] = inf;
f[i] = lower_bound(d+1 , d+1+len+1 , a[i]+x)-d;
*lower_bound(d+1,d+1+len+1,a[i]) = a[i];
len = d[len+1] ? len+1 : len;
}
mem(d,0);
for(int i=n;i>0;--i)
{
g[i] = lower_bound(d+1 , d+1+len+1 , a[i] , greater())-d;
*lower_bound(d+1,d+1+len+1,a[i],greater()) = a[i];
len = d[len+1] ? len+1 : len;
}
for(int i=1;i<=n;i++) ans = max(ans , f[i] + g[i] - 1);
printf("%d\n",ans);
return 0;
}