USACO 2017 FEB Platinum nocross DP

  题目大意

    上下有两个长度为n、位置对应的序列A、B,其中数的范围均为1~n。若abs(A[i]-B[j]) <= 4,则A[i]与B[j]间可以连一条边。现要求在边与边不相交的情况下的最大的连边数量。n <= 10^5。

  在Gold里,此题的数据范围是1000,我们完全可以用简单的最长公共连续子序列的DP方法来做。

  范围大了之后,可以观察到对于一个数A[i],它所能转移的状态最多只有9个,那么就可以顺序扫描A数组,设F[i][j]表示当前连得最后一条边为(A[i],B[to[i][j]])的最优解。to[i][j]即A[i]能转移到的B[i]的位置(顺序从小到大)。建立一棵线段树,表示最后连的边中的数B在B数组的位置时,所能得到的最优解。F[i][j]就可以直接logn查询,logn把F[i][j]更新到线段树中。

  

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>

using namespace std;

const int maxn = 100005;
int n, a[maxn], b[maxn];
int f[maxn][12], cnt[maxn], to[maxn];
int adj[maxn][12];
struct Tree
{
    int maxv[maxn*4];
    Tree()
    {
        memset(maxv, 0, sizeof(maxv));
    }
    void pushup(int rt)
    {
        maxv[rt] = max(maxv[rt<<1], maxv[(rt<<1)+1]);
    }
    void update(int rt, int l, int r, int p, int d)
    {
        if (l == r)
        {
            maxv[rt] = max(maxv[rt], d);
            return ;
        }
        int mid = (l+r)>>1;
        if (p <= mid)
            update(rt<<1, l, mid, p, d);
        else
            update((rt<<1)+1, mid+1, r, p, d);
        pushup(rt);
    }
    int query(int rt, int l, int r, int L, int R)
    {
        if (L <= l && r <= R)
            return maxv[rt];
        int mid = (l+r)>>1, ret = 0;
        if (L <= mid)
            ret = max(ret, query(rt<<1, l, mid, L, R));
        if (R > mid)
            ret = max(ret, query((rt<<1)+1, mid+1, r, L, R));
        return ret;
    }
}T;

int main()
{
    freopen("nocross.in", "r", stdin);
    freopen("nocross.out", "w", stdout);
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &b[i]), to[b[i]] = i;
    for (int i = 1; i <= n; ++i)
    {
        int l = a[i]-4, r = a[i]+4;
        if (l < 0) 
            l = 1;
        if (r > n)
            r = n;
        cnt[i] = 0;
        for (int j = l; j <= r; ++j)
            adj[i][++cnt[i]] = to[j];
        sort(adj[i]+1, adj[i]+cnt[i]+1);
    }
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= cnt[i]; ++j)
            if (adj[i][j]-1 >= 1)
                f[i][j] = T.query(1, 1, n, 1, adj[i][j]-1)+1;
            else
                f[i][j] = 0;
        for (int j = 1; j <= cnt[i]; ++j)
            if (adj[i][j]-1 >= 1)
                T.update(1, 1, n, adj[i][j], f[i][j]);
    }
    printf("%d\n", T.maxv[1]);
    return 0;
}

 

  

posted @ 2017-02-15 21:00  Splay  阅读(647)  评论(0编辑  收藏  举报