3085 蚱蜢

Task
n*n的矩阵,初始位置在x,y,按照以下要求遍历:
下一步为x1,y1
① |x-x1|=1,|y-y1|>1或|y-y1|=1,|x-x1|>1
② Val[x1][y1]>val[x][y]
求最多遍历的位置个数。
50% n<=100
80% n<=1000
100% n<=1500,val<=1e6

Solution
观察条件②每个点向权值大的数转移。假如在序列中,相当于求单调递增子序列的最大长度(LIS)。题目是LIS的变形,转化成在矩阵中,有转移的限制条件①。

如果从点( i ,j )转移,如果不是从起点到( i , j )的最长长度,一定不是最优的,用这个长度更新其他点是没有意义的。因此对于每个点( I , j )我们需要的信息只有从源点到达(I,j)的最长长度(最优的信息),可以用dp处理。

50分暴力dp
对于两个限制条件,先确定一个,再枚举另一个。
复杂度O(n^3)
当前点只可能从权值比它小的点转移过来,因此按照权值排序,处理当前i点,所有权值比它小的点都已经处理完毕,枚举符合条件的点,转移即可。

70分树状数组前后缀优化:
如果画出转移的图形,可以发现转移的是四周4行4列的前缀后缀信息。
树状数组可以胜任前缀后缀信息的维护。

100分正难则反 存最值
正难则反,不是考虑哪些点能转移,而是考虑哪些点不能转移,会发现,当前点对于每一行每一列不能转移的只有3个点。如果对于 每一行每一列都存最大的前4个,根据抽屉原理,必定可以找到一个转移,而且是最大的。

对于权值相同会互相影响结果的一段区间,为了避免这种影响,我们采用这一段区间都先查询完,再更新,更新时采用插入排序,找到第一个小的点,后面的点都往后移动一格,这样比每次swap更优。

这里的排序可以采用计数排序,O( n )!!
Cnt[i]表示权值<=i的个数,A[cnt[i]–]=i;
适用于权值种类不大(可以离散)的排序。

const int M=1505;
int rx[]={-2,-2,-1,1,2,2,1,-1},ry[]={1,-1,-2,-2,-1,1,2,2},rz[]={0,0,0,0,1,1,1,1};
int n;
/* 树状数组优化前缀 */
struct Tree{
    int bit[2][M];
    inline void update(int x,int c,int v){
        if(c)x=n-x+1;//后缀
        while(x<n){
            MAX(bit[c][x],v);
            x+=lowbit(x);
        } 
    }
    inline int query(int x,int c){
        int mx=0;
        if(c)x=n-x+1;
        while(x>0){
            MAX(mx,bit[c][x]);
            x-=lowbit(x);
        }
        return mx;//!!
    }
}Tx[M],Ty[M];//450w
struct node{
    int x,y,v;
    bool operator<(const node &t)const{
        return v<t.v;
    }
};
struct P70{
    int A[M][M],dp[M*M],cnt[1000005];
    int sx,sy,tot;
    node C[M*M];//625w
    inline void Sort(){//
        int i,j,k;
        rep(i,2,tot)cnt[i]+=cnt[i-1];//小于等于i的个数 
        rep(i,1,n)
            rep(j,1,n)
                C[cnt[A[i][j]]--]=(node){i,j,A[i][j]};
    }
    inline void input(){
        int i,j,k;
        rd(n);rd(sx);rd(sy);
        rep(i,1,n){
            rep(j,1,n){
                rd(A[i][j]);
                cnt[A[i][j]]++;
                MAX(tot,A[i][j]);
            }
        }
        Sort();
    }
    inline bool check(int x,int y){return x>=1&&x<=n&&y>=1&&y<=n;}
    inline void solve(){
        int ii,i,j,k,mx,x,y,nx,ny,ans=0,res,r;
        input();
        tot=n*n;
        rep(i,1,tot)
            if(C[i].x==sx&&C[i].y==sy){ii=i;break;}
        for(k=ii;k<=tot;k=r+1){
            r=k;
            while(C[r].v==C[k].v)r++;r--;//权值相同一起处理 
            rep(i,k,r){
                mx=0;x=C[i].x,y=C[i].y;
                rep(j,0,7){
                    nx=x+rx[j],ny=y+ry[j];
                    if(!check(nx,ny))continue;
                    if(j==0||j==1||j==4||j==5)MAX(mx,Ty[ny].query(nx,rz[j]));
                    else MAX(mx,Tx[nx].query(ny,rz[j]));
                }
                dp[i]=0;
                if(mx||(x==sx&&y==sy))dp[i]=mx+1;
                MAX(ans,dp[i]);
            }
            rep(i,k,r){
                x=C[i].x,y=C[i].y;
                if(dp[i]){
                    Tx[x].update(y,0,dp[i]);
                    Tx[x].update(y,1,dp[i]);
                    Ty[y].update(x,0,dp[i]);
                    Ty[y].update(x,1,dp[i]);
                }
            }

        }
        printf("%d\n",ans);
    }
}P70;

100分:

const int M=1503;
struct node1{
    int x,y,v;
    bool operator<(const node1 &t)const{
        return v<t.v;
    }
}C[M*M];
/*  每一行,列存前4个 */
struct node2{
    int id,v;
    inline void clear(){id=v=0;}
}T[2][M][5];
int dir[]={-1,1};
int A[M][M],dp[M*M],cnt[1000003];
int n,tot,sx,sy;
void Sort(){//计数排序 
    int i,j,k;
    rep(i,1,tot)cnt[i]+=cnt[i-1];//<=i的个数
    rep(i,1,n)
        rep(j,1,n)C[cnt[A[i][j]]--]=(node1){i,j,A[i][j]};
}
void input(){
    int i,j,k;
    rd(n);rd(sx);rd(sy);
    rep(i,1,n)
        rep(j,1,n){
            rd(A[i][j]);
            cnt[A[i][j]]++;
            MAX(tot,A[i][j]);
        }
    Sort();
}
inline int query(int c,int x,int y){
    int i;
    if(x<=0||x>n||y<=0||y>n)return 0;
    rep(i,0,3)
        if(abs(T[c][x][i].id-y)>1)return T[c][x][i].v;
    return 0;
}
inline void update(int c,int x,int y,int v){
    int i,pos=-1;
    rep(i,0,3)
        if(T[c][x][i].v<=v){pos=i;break;}
    if(~pos){
        per(i,3,pos+1)T[c][x][i]=T[c][x][i-1];
        T[c][x][pos]=(node2){y,v};
    }
}
void solve(){
    int i,j,k,r,ii,mx,x,y,ans=0;
    rep(i,0,1)
        rep(j,0,n)
            rep(k,0,4)T[i][j][k].clear();
    rep(i,1,n*n)
        if(C[i].x==sx&&C[i].y==sy){ii=i;break;}
    dp[ii]=1;
    for(k=ii;k<=n*n;k=r+1){
        r=k;//val相同 同时查询 更新 
        while(C[r].v==C[k].v)++r;--r;
        rep(i,k,r){
            x=C[i].x,y=C[i].y,mx=0;
            rep(j,0,1){
                MAX(mx,query(0,x+dir[j],y));
                MAX(mx,query(1,y+dir[j],x));
            }
            if(mx)dp[i]=mx+1;
        }
        rep(i,k,r){
            if(dp[i]){
                update(0,C[i].x,C[i].y,dp[i]);
                update(1,C[i].y,C[i].x,dp[i]);
            }
            MAX(ans,dp[i]);
        }
    }
    printf("%d\n",ans);
}
int main(){
    input();
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值