bzoj3173: [Tjoi2013]最长上升子序列

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=3173

题解

  我跟你讲啊,这题我死磕了一下午,就是想不出来怎么构造最后的那个序列。
  还是看题解比较爽。
  开个 BIT ,每个位置加一,表示所有位置都放上数了。
  然后我们倒着搞,如果一个数你读入的是 pos ,那我就找前 pos 1 ,最后一个1所在的位置就是我这个数最终的位置,搞完这个数之后把它最终位置上的那个 1 删掉。
  考虑下这个算法的正确性,搞一个数的时候,它后面的那些数的正确位置上都是0,那么这个数面临的就是第一个数到这个数的位置上都是 1 的情况,你又知道它是第pos个,所以就是这样啦。
  然后最后的求答案我比较傻用的 CDQ 分治,看了题解发现其实只要普通求 LIS 就行了…
  时间复杂度是 O(nlogn)
   BIT k <script type="math/tex" id="MathJax-Element-1001">k</script>大有神奇的技巧,具体看程序

代码

//平衡树 + CDQ分治 + 树状数组 
#include <cstdio>
#include <algorithm>
#define maxn 100010
#define lowbit(x) (x&-x)
using namespace std;
int bit[maxn], N, final[maxn], mini[maxn], ndtot;
struct number{int pos, w, f;}num[maxn];
inline int read(int x=0)
{
    char c=getchar();
    while(c<48 or c>57)c=getchar();
    while(c>=48 and c<=57)x=(x<<1)+(x<<3)+c-48, c=getchar();
    return x;
}
inline bool operator<(number a, number b){return a.w<b.w;}
void bitadd(int pos, int v)
{for(;pos<=N;pos+=lowbit(pos))bit[pos]+=v;}
void bitins(int pos, int v)
{for(;pos<=N;pos+=lowbit(pos))bit[pos]=max(bit[pos],v);}
int bitmax(int pos)
{int ans=0;for(;pos;pos-=lowbit(pos))ans=max(ans,bit[pos]);return ans;}
void biterase(int pos)
{for(;pos<=N;pos+=lowbit(pos))bit[pos]=0;}
int bitkth(int k)
{
    int res=0, cnt=0, i;
    for(i=17;~i;i--)
    {
        res+=1<<i;
        if(res>N or cnt+bit[res]>=k)res-=1<<i;
        else cnt+=bit[res];
    }
    return res+1;
}
void init()
{
    int i, j, t;
    N=read();
    for(i=1;i<=N;i++)num[i].w=i, num[i].pos=read()+1;
    for(i=1;i<=N;i++)bitadd(i,1);
    for(i=N;i>=1;i--)
    {
        t=bitkth(num[i].pos);
        bitadd(t,-1);
        num[i].pos=t;
    }
}
void cdq(int l, int r)
{
    int mid=l+r>>1, i, j;
    if(l==r)return;
    cdq(l,mid);
    for(i=l;i<=mid;i++)bitins(num[i].pos,num[i].f);
    for(i=mid+1;i<=r;i++)num[i].f=max(num[i].f,bitmax(num[i].pos)+1);
    for(i=l;i<=mid;i++)biterase(num[i].pos);
    cdq(mid+1,r);
}
int main()
{
    int ans=0;
    init();
    for(int i=1;i<=N;i++)num[i].f=1;
    cdq(1,N);
    for(int i=1;i<=N;i++)
    {
        ans=max(ans,num[i].f);
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值