update:2021 06 14 更改了半全角,图片,LaTeX。
ETHANK 大佬已经写过题解,但是对于没有想过来的 friends 可能会有点迷糊,故以此帖加以补充。
通过读题可知我们需要修改的是一个连续区间 [ l , r ] [l,r] [l,r],由于增加值相等,所以区间内的元素相对大小不改变,也就是说,这次修改只会对 [ r + 1 , n ] [r+1,n] [r+1,n] 造成影响。但修改的范围和增加值我们都不知道,这也是本题难点。
我们不妨换个角度思考,修改操作的作用其实就是把某一段高度高的区间降下来,与前后剩余区间的某些元素构成 LIS,或是提升某一段区间的高度,与上面的情况同理。
设增加值为 x,前面一段区间 A 的 LIS 的终点为 a,后面一段区间 B 的 LIS 起点为 b。
-
当 a < b \operatorname{a}<\operatorname{b} a<b,二者直接合并。
-
当 a < b + x \operatorname{a}<\operatorname{b}+\operatorname{x} a<b+x,此时相当于提升 B ,两者合并。
如图:(黑色代表 A,橙色代表 B,蓝色代表 [ r + 1 , n r+1,n r+1,n])。
不难发现,a<b+x 与 a-x<b 等价。
如图:
又因为区间内部相对大小不变,所以 A 的减小值肯定越大越好,更有可能与后面构成新的 LIS,而 B 的增加值就不一定了,因为有可能增加了反而超过了后面可充当新 LIS 后半段的区间的元素大小,即能减少到 d,但不一定能增加到 d。
如图:
既然前面区间和我这个区间一起改变后我们之间不会有影响,但可能对后面的区间产生影响,那么肯定每次变动整个前缀最优。
答案即为每次修改后 [ 1 , n ] [1,n] [1,n] 的 LIS 的最大值。
暴力的时间复杂度为 O ( n 2 log n ) O(n^2 \log n) O(n2logn)。
对于优化,我们可以用动态 LIS ,时间复杂度为 O ( n log n ) O(n \log n) O(nlogn) 。
我们也可以用 dp[i] 存储以 i 为终点的 LIS,pd[i] 存储 a[i] 减小后,作为起点在原序列中的 LIS ,则:
ans[i] = dp[i] + pd[i] − 1 \operatorname{ans[i]}=\operatorname{dp[i]}+\operatorname{pd[i]}-1 ans[i]=dp[i]+pd[i]−1
dp[i] 和 pd[i] 都可以用 BIT 在 O ( n log n ) O(n \log n) O(nlogn) 的时间内求到,具体操作可以看代码。
不要忘记离散化。
Code:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN=2e5+5;
#define int long long
int n,len;
long long a[MAXN<<1],x,BIT[MAXN<<1],b[MAXN<<1],dp[MAXN],pd[MAXN],ans,val[MAXN],Count[MAXN];//Count[i]的update即把a[i]加进原序列
int lowbit(int x)
{
return x&-x;
}
void update(int x,long long k)//正向LIS
{
for(int i=x;i<=len;i+=lowbit(i))
{
BIT[i]=max(BIT[i],k);
}
}
void update_op(int x,long long k)//反向LIS
{
for(int i=x;i;i-=lowbit(i))
{
BIT[i]=max(BIT[i],k);
}
}
long long sum(int x)
{
long long ans=0;
for(int i=x;i;i-=lowbit(i))
{
ans=max(ans,BIT[i]);
}
return ans;
}
long long sum_op(int x)
{
long long ans=0;
for(int i=x;i<=len;i+=lowbit(i))
{
ans=max(ans,BIT[i]);
}
return ans;
}
signed main()
{
scanf("%lld%lld",&n,&x);
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
a[i]+=x;
b[i]=a[i];
b[i+n]=a[i]-x;
}
sort(b+1,b+1+2*n);
len=unique(b+1,b+1+2*n)-b-1;
for(int i=1;i<=n;i++)
{
val[i]=lower_bound(b+1,b+1+len,a[i]-x)-b;
a[i]=lower_bound(b+1,b+1+len,a[i])-b;
}//离散化
for(int i=1;i<=n;i++)
{
dp[i]=sum(a[i]-1)+1;
update(a[i],dp[i]);
}
memset(BIT,0,sizeof(BIT));
for(int i=n;i>=1;i--)
{
pd[i]=sum_op(val[i]+1)+1;//因为修改的是前缀,后面的无影响
Count[i]=sum_op(a[i]+1)+1;//本身要为原来数组的后缀做贡献
update_op(a[i],Count[i]);
ans=max(ans,pd[i]+dp[i]-1);
}
printf("%lld\n",ans);
return 0;
}
E n d End End