[USACO17FEB] Why Did the Cow Cross the Road II P [树状数组优化dp]

Why Did the Cow Cross the RoadWhy\ Did\ the\ Cow\ Cross\ the\ Road

题目描述见链接 .


\color{red}{正解部分}

F[i,j]F[i, j] 表示 AA 序列前 ii 项, BB 序列前 jj 项 的最大匹配数,

  • i,ji,j 不相连: F[i,j]=max(F[i1,j],F[i,j1])F[i, j] = \max(F[i-1,j],F[i,j-1])
  • i,ji, j 相连: F[i,j]=max(F[i1,j1]+1)F[i, j] = \max(F[i-1,j-1]+1)

经典的最长公共子序列算法 .

考虑优化, 发现 iijj 相连当且仅当 AiBj4|A_i-B_j| \le 4, 于是在 ii 确定的情况下, jj 最多只有 99 个可能的取值,

所以可以记下 BjB_j 的位置 posBjpos_{B_j} 从小到大枚举 ii, 再枚举 99 种可能的 BjB_j,
F[i1,0 ... posBj11]+1F[i-1, 0 \ ...\ pos_{B_j-1}-1]+1 转移过来, 使用 树状数组 优化 最大前缀 即可实现 O(9×NlogN)O(9\times N \log N) .


\color{red}{实现部分}

#include<bits/stdc++.h>
#define reg register

const int maxn = 100005;

int N;
int A[maxn];
int B[maxn];
int tmp[maxn];
int pos[maxn];

struct Bit_Tree{
        int v[maxn], lim;
        void Add(int k, int x){
                while(k <= lim){
                        v[k] = std::max(v[k], x);
                        k += k&-k;
                }
        }
        int Query(int k){
                if(k <= 0) return 0;
                int s = 0;
                while(k){
                        s = std::max(s, v[k]);
                        k -= k&-k;
                }
                return s;
        }
} bit_t;

int main(){
        scanf("%d", &N);
        for(reg int i = 1; i <= N; i ++) scanf("%d", &A[i]);
        for(reg int j = 1; j <= N; j ++) scanf("%d", &B[j]), pos[B[j]] = j;
        bit_t.lim = N;
        for(reg int i = 1; i <= N; i ++){
                int dlim = std::max(1, A[i]-4), ulim = std::min(N, A[i]+4);
                for(reg int j = dlim; j <= ulim; j ++) tmp[j] = bit_t.Query(pos[j]-1) + 1;
                for(reg int j = dlim; j <= ulim; j ++) bit_t.Add(pos[j], tmp[j]);
        }
        printf("%d\n", bit_t.Query(N));
        return 0;
}
posted @ 2019-10-30 23:33  XXX_Zbr  阅读(210)  评论(0编辑  收藏  举报