您的位置:首页 > 其它

bzoj 3173: [Tjoi2013]最长上升子序列

2017-10-19 20:18 351 查看

题意:

每次将i插入当前第k个数字后面,求最长上升子序列。

题解:

假如这题不是插入,而是直接说放到第k位,且保证k不相同,那么就很好做了。

设f[i]表示以第i位为结尾的最长上升子序列。

加入一个值时就统计下它前面的最长上升子序列,加1就好了。因为他是最大的,所以显然对其他f值没有影响。

于是关键是怎么求出原序列。

如果强制在线的话直接平衡树无脑上就好了nlogn完全不虚。

但是可以离线,所以有一种很简便,但挺难想的做法。

将给出的序列反过来插入,对于i,假设i+1~n的数都插好了,怎么求i插在哪呢。

考虑二分,i插入的地方为mid,那么i被向后推了mid-X[i]格。换句话说,在已经插入的数中,mid前要有mid-X[i]个数。(因为无论怎么推,数与数间的相对位置是不会变的,所以插入的时候一定就在i前面)。

所以二分的判定条件是:
getsum(mid)<=mid-x


getsum是树状数组求和。

至于为什么满足二分性,可以将两个函数大概的图像画出来,就显然了。

code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
int a[100010],n,tr[100010];
int lowbit(int x){return x&(-x);}
void change(int k,int c)
{
for(int i=k;i<=n;i+=lowbit(i))
tr[i]=max(tr[i],c);
}
int get(int k)
{
int ans=0;
for(int i=k;i>=1;i-=lowbit(i))
ans=max(ans,tr[i]);
return ans;
}
void Change(int k,int c)
{
for(int i=k;i<=n;i+=lowbit(i))
tr[i]+=c;
}
int getsum(int k)
{
int ans=0;
for(int i=k;i>=1;i-=lowbit(i))
ans+=tr[i];
return ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),a[i]++;
for(int i=n;i>=1;i--)
{
int x=a[i];
int l=a[i],r=n;
while(l<=r)
{
int mid=(l+r)/2;
if(getsum(mid)<=mid-x) a[i]=mid,r=mid-1;
else l=mid+1;
}
Change(a[i],1);
}
int ans=0;
memset(tr,0,sizeof(tr));
for(int i=1;i<=n;i++)
{
int k=a[i],t=get(k-1)+1;
ans=max(ans,t);change(k,t);
printf("%d\n",ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: