hdu 5773 The All-purpose Zero(Multi J,给你n个数(n<=1e5),每个数小于等于1000000,其中数为0的那些数可以变成其他任意的数,问变化之后的最长上升子序)

来源:互联网 发布:解放战争胜利原因知乎 编辑:程序博客网 时间:2024/06/05 03:08

传送门:hdu 5773 The All-purpose Zero

题意:给你n个数(n<=1e5),每个数小于等于1000000,其中数为0的那些数可以变成其他任意的数,问变化之后的最长上升子序列。


思路一:
首先猜想一个结论:那些为0的数一定全部在最长上升子序列中
反证法:假设至少一个0不在最长上升子序列中,那么将这个0的后面一个位置的数替换为0,最长上升子序列的长度一定不变。


那么我们可以想到一个做法,每遇到一个0,就将其后面的数全部减一,表示这个0必取,那么剩下的数的最长上升子序列+0的个数就是答案。


#include<bits/stdc++.h>using namespace std;const int maxn=101000;int a[maxn],b[maxn];int main(){    int _,n;    scanf("%d",&_);    for(int case1=1;case1<=_;case1++){        scanf("%d",&n);        int pre=0,cnt=0;        for(int i=1;i<=n;i++){            scanf("%d",&a[i]);            if(a[i]==0)                pre++;            else                a[i]-=pre,a[++cnt]=a[i];        }        int len=0,pos;        if(cnt>=1){            b[1]=a[1];            len=1;            for(int i=2;i<=cnt;i++){                if(b[len]<a[i])                    len++,b[len]=a[i];                else                    pos=lower_bound(b+1,b+len+1,a[i])-b,b[pos]=a[i];            }        }        printf("Case #%d: %d\n",case1,len+pre);    }    return 0;}

思路二:
dp[i]是表示上升子序列长度为i的最小值
如果有一个0要插入,那就dp[i]+1->dp[i+1],然后dp[1]=负无穷
->序列右移->线段树上预留出很多位置,起点左移

#include<bits/stdc++.h>using namespace std;const int maxn=200100;const int inf=0x3f3f3f3f;int lazy[maxn<<2],maxv[maxn<<2],a[maxn];#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1int L,R,Midpoint,tot,v; //Midpoint表示刚开始的起点的位置void pushup(int rt){    maxv[rt]=max(maxv[rt<<1],maxv[rt<<1|1]);}void build(int l,int r,int rt){    lazy[rt]=0;    if(l==r){        if(l<Midpoint)            maxv[rt]=-inf;        else            maxv[rt]=inf;        return ;    }    int m=(l+r)>>1;    build(lson);    build(rson);    pushup(rt);}void pushdown(int rt){    maxv[rt<<1]+=lazy[rt],maxv[rt<<1|1]+=lazy[rt];    lazy[rt<<1]+=lazy[rt],lazy[rt<<1|1]+=lazy[rt];    lazy[rt]=0;}void update(int l,int r,int rt){    if(l==r){        maxv[rt]=v;        return ;    }    pushdown(rt);    int m=(l+r)>>1;    if(L<=m&&maxv[rt<<1]>=v)        update(lson);    else        update(rson);    pushup(rt);}int query(int l,int r,int rt){    if(L<=l&&R>=r)        return maxv[rt];    int m=(l+r)>>1;    if(L<=m)        return query(lson);    else        return query(rson);}int main(){    int _,n;    scanf("%d",&_);    for(int case1=1;case1<=_;case1++){        scanf("%d",&n);        for(int i=1;i<=n;i++)            scanf("%d",&a[i]);        Midpoint=n+5,tot=n*2+10;        build(1,tot,1);        for(int i=1;i<=n;i++){            if(a[i]==0){                ++lazy[1];                L=--Midpoint,R=L,v=-inf;                update(1,tot,1);            }            else                v=a[i],L=n+5,R=tot,update(1,tot,1);        }        int ans=0;        for(int i=Midpoint;i<=tot;i++){            L=i,R=i;            if(query(1,tot,1)>=inf)                break;            ans=i-Midpoint+1;        }        printf("Case #%d: %d\n",case1,ans);    }    return 0;}

思路三:
cdq+扫描线+树状数组
假设我们现在要求len[x]=max( len[i]+min(a[x]-a[i]-1,pre[x]-pre[i]) )+1
假设a[x]>a[i],我们对min(a[x]-a[i]-1,pre[x]-pre[i])进行讨论,维护一个三维偏序
1.a[x]-a[i]-1<=pre[x]-pre[i]-> a[x]-pre[x]-1<=a[i]-pre[i],这时候加上的最大值便为len[i]+(a[x]-a[i]-1)

2.a[x]-a[i]-1>pre[x]-pre[i]-> a[x]-pre[x]-1>a[i]-pre[i],这时候加上的最大值便为len[i]+(pre[x]-pre[i])

#include<bits/stdc++.h>using namespace std;const int inf=0x3f3f3f3f;const int maxn=200100;int m,pre[maxn],a[maxn],B[maxn],b[maxn],ans,T,len[maxn];int qa[maxn],qb[maxn],pos0[maxn],pos1[maxn],maxv0[maxn],maxv1[maxn],tot,num,C[maxn];bool cmp(int x,int y){    return a[x]<a[y];}void add0(int x,int y){    for(int i=x;i>0;i-=(i&-i)){        if(pos0[i]!=T)            maxv0[i]=y,pos0[i]=T;        else            maxv0[i]=max(maxv0[i],y);    }}void add1(int x,int y){    for(int i=x;i<=num;i+=(i&-i)){        if(pos1[i]!=T)            maxv1[i]=y,pos1[i]=T;        else            maxv1[i]=max(maxv1[i],y);    }}void ask0(int &ans,int y,int x){    while(x<=num){        if(pos0[x]==T)            ans=max(ans,y+maxv0[x]);        x+=(x&-x);    }}void ask1(int &ans,int y,int x){    while(x>0){        if(pos1[x]==T)            ans=max(ans,y+maxv1[x]);        x-=(x&-x);    }}void solve(int l,int r){    if(l==r){        len[l]++;        return ;    }    int mid=l+r>>1;    solve(l,mid);    int Count_a=0,Count_b=0;    for(int i=l;i<=mid;i++)        qa[Count_a++]=i;    for(int i=mid+1;i<=r;i++)        qb[Count_b++]=i;    sort(qa,qa+Count_a,cmp),sort(qb,qb+Count_b,cmp);    int id=0,i;    for(T++,i=0;i<Count_b;i++){        while(id<Count_a&&a[qa[id]]<a[qb[i]]){            add0(b[qa[id]],len[qa[id]]-a[qa[id]]-1);//比b[qa[id]]小的位置加上len[qa[id]]-a[qa[id]]-1            add1(b[qa[id]],len[qa[id]]-pre[qa[id]]);//相反            id++;        }        int num1=lower_bound(B+1,B+num+1,a[qb[i]]-pre[qb[i]]-1)-B;        ask0(len[qb[i]],a[qb[i]],num1);        ask1(len[qb[i]],pre[qb[i]],num1-1);    }    solve(mid+1,r);}int main(){    int _,n;    scanf("%d",&_);    for(int case1=1;case1<=_;case1++){        scanf("%d",&n);        a[m=1]=-inf,T=0;        int Count=0,x;        memset(len,0,sizeof(len));        memset(pre,0,sizeof(pre));        memset(pos0,0,sizeof(pos0));        memset(pos1,0,sizeof(pos1));        for(int i=1;i<=n;i++){            scanf("%d",&x);            if(x==0)                Count++;            else                pre[++m]=Count,a[m]=x;        }        pre[++m]=Count,a[m]=inf,tot=0;        for(int i=1;i<=m;i++)            B[++tot]=b[i]=a[i]-pre[i],B[++tot]=b[i]-1;        sort(B+1,B+tot+1);        num=unique(B+1,B+tot+1)-B-1;        for(int i=1;i<=m;i++)            b[i]=lower_bound(B+1,B+num+1,b[i])-B;        solve(1,m);        printf("Case #%d: %d\n",case1,len[m]-2);    }    return 0;}
0 0
原创粉丝点击