前言
题目大意:
题目链接:https://jzoj.net/senior/#main/show/6310
给定整数和,以及一个大小为的序列。
你可以选择一个区间,然后令,其中满足。
要求最大化的最长上升子序列的长度,并输出该值。
思路:
首先有一个显然的性质:如果我们要把一段区间增加,那么显然这段区间右端点为时最优。
因为如果我们要增加区间,那么如果我们把往右移一位,显然不会把答案变小。所以就可以移至最右。
那么这样我们就可以证明出如果区间需要上移,那么显然往上移动越多可以对答案造成更大的贡献。所以我们移动就将一段区间移动格。
那么如果我们需要把区间往下移动呢?和前面一样,显然把区间扩张成会最优,那么把往下移动就相当于把向上移动。所以可以不用处理。
同理,如果取原来的(不移动任何)也就相当于全部移动,所以也可以不用再去计算了。
那么如何计算呢?
假设现在处理末位为的情况,那么我们就要在中找到一个权值不超过且尽量大
的来转移。
那么我们可以建立一棵权值线段树,区间表示权值在中的的最大值。这样从前往后从后往前做时只要先转移再插入即可。
那么我们就枚举在哪一个位置开始加,然后求出这个位置前后的,一加就是答案。
这道题,所以动态开点就行了。
但是这样会
愉快的每一个搜错了至少一个
我们发现其中很多位置,然后
哦权值线段树的空间复杂度是啊。
于是把空间开大倍。
然后
输出了一下空间
于是就开始了漫长的卡空间之路。。。
- 我们发现,开两棵线段树分别计算两次是没必要的,所以我们就删掉一棵线段树,两次计算之间清零即可。
- 清零的标记占了的空间,考虑直接暴力重构整棵树。
试探性的把树的大小缩小
最终,线段树大小开到时,空间总算卡进了!!
调试了一下,发现过程中出现了负数。
原来是因为,最大在线段树内可能出现的数为,所以线段树开到后,就炸了。。。
因为这些我调试了一下午,还不如去打离散化的线段树。
时间复杂度,常数超级无敌大。
代码:
#include <cstdio>
#include <string>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=200010,Inf=2e9;
int n,m,ans,root,a[N],f[N];
struct Treenode
{
int lc,rc,maxn;
};
struct Tree
{
Treenode tree[N*110];
int tot;
void clr(int x)
{
if (tree[x].lc) clr(tree[x].lc);
if (tree[x].rc) clr(tree[x].rc);
tree[x].lc=tree[x].rc=tree[x].maxn=0;
}
int ask(int &x,int nowl,int nowr,int l,int r)
{
if (l>r) return 0;
if (!x) x=++tot;
if (nowl==l && nowr==r)
return tree[x].maxn;
int mid=((ll)nowl+(ll)nowr)>>1;
if (r<=mid) return ask(tree[x].lc,nowl,mid,l,r);
if (l>mid) return ask(tree[x].rc,mid+1,nowr,l,r);
return max(ask(tree[x].lc,nowl,mid,l,mid),ask(tree[x].rc,mid+1,nowr,mid+1,r));
}
void insert(int &x,int nowl,int nowr,int k,int val)
{
if (!x) x=++tot;
if (nowl==k && nowr==k)
{
tree[x].maxn=max(tree[x].maxn,val);
return;
}
int mid=((ll)nowl+(ll)nowr)>>1;
if (k<=mid) insert(tree[x].lc,nowl,mid,k,val);
else insert(tree[x].rc,mid+1,nowr,k,val);
tree[x].maxn=max(tree[tree[x].lc].maxn,tree[tree[x].rc].maxn);
}
}Tree;
int read()
{
int d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch))
d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
int main()
{
freopen("glo.in","r",stdin);
freopen("glo.out","w",stdout);
n=read(); m=read();
for (int i=1;i<=n;i++)
a[i]=read();
for (int i=1;i<=n;i++)
{
f[i]=Tree.ask(root,1,Inf,1,a[i]+m-1)+1;
Tree.insert(root,1,Inf,a[i],Tree.ask(root,1,Inf,1,a[i]-1)+1);
}
root=0; Tree.tot=0; Tree.clr(1);
for (int i=n;i>=1;i--)
{
Tree.insert(root,1,Inf,a[i]+m,Tree.ask(root,1,Inf,a[i]+m+1,Inf)+1);
ans=max(ans,f[i]+Tree.ask(root,1,Inf,a[i]+m+1,Inf));
}
printf("%d",ans);
return 0;
}