zoukankan      html  css  js  c++  java
  • 2017 多校4 Security Check

    2017 多校4 Security Check

    题意:

    (A_i)(B_i)两个长度为(n)的队列过安检,当(|A_i-B_j|>K)的时候, (A_i和B_j)是可以同时过安检的,过安检必须按照队列的顺序,问你两个队列过完安检最少花费的时间
    (1<=n<=6e4)
    (1<=A_i,B_i<=n)

    题解:

    (f_{i,j})
    表示仅考虑(a[1..i])(b[1..j])时,最少需要多少时间。

    (|a_i-b_j|>k)
    ​,则(f_{i,j}=f_{i-1,j-1}+1)否则(f_{i,j}=min(f_{i-1,j},f_{i,j-1})+1)

    注意到后者只有(O(nk))个,可以暴力DP,前者可以通过二分找到最大的(t),满足(i,j)往前(t)个均不冲突,然后再从某个后者状态转移过来。

    时间复杂度(O(nklog n))

    这个(dp)就跟求最长公共子序列的dp一样,很容易想到(O(n^{2}))的做法,优化就不容易想到了,想到了也不知道如何实现

    对于(|A_i-B_j|<=K)的时候 可以暴力(dp)
    否则在(i)的左边找一个最接近(i)(t) 满足(|A_t-B_{j-i+t}|<=K)
    那么在(t<p<=i) 这一段 都是满足(|A_p-B_{j-i+p}|>K)他们同时过安检显然是最优的,直接算即可,这样下次有跳跃到了(<=K)的情况,所以只需要(做dp) 计算(<=K)的情况

    如何找到这个(t)呢,开始我直接写了一发二分下标,WA,没仔细思考,这样二分下标不具有单调性。去网上看了看别人的写法,是把所有(i-j)差值为(x)而且(|A_i-B_j|<=K)(i)存起来,然后去二分这个(i),好巧的做法,因为(i,j)一直往左走,走到(t),差值都是相同的,这样是可行的。

    #include<bits/stdc++.h>
    #define LL long long
    #define P pair<int,int>
    using namespace std;
    const int N = 6e4 + 10;
    int read(){
        int x = 0;
        char c = getchar();
        while(c < '0' || c > '9') c = getchar();
        while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
        return x;
    }
    int n,k;
    int a[N],b[N],pos[N];
    vector<int> v[2 * N];///下标差值为x且绝对值小于等于k的 a_i的位置
    int dp[N][22];
    int Find(int i,int j){///i左边找到第一个x 使得abs(a[x] - b[j - i + x])<=k
       int x = upper_bound(v[i - j + n].begin(),v[i - j + n].end(),i) - v[i - j + n].begin() - 1;
       if(x < 0) return 0;
       return v[i-j+n][x];
    }
    int dfs(int i,int j){
        if(!i || !j) return i + j;
        if(abs(a[i] - b[j]) <= k){
            int &res = dp[i][a[i] - b[j] + k];
            if(res != -1) return res;
            return res = min(dfs(i-1,j),dfs(i,j-1)) + 1;
        }
        int x = Find(i,j);
        return dfs(x,max(0,j - i + x)) + i - x;
    }
    int main(){
    
        int T;
        T = read();
        while(T--){
            n = read(),k = read();
            for(int i = 1;i <= n;i++) a[i] = read();
            for(int i = 1;i <= n;i++) b[i] = read(),pos[b[i]] = i;
            for(int i = 0;i <= 2 * n;i++) v[i].clear();
            for(int i = 1;i <= n;i++){
                for(int j = max(1,a[i] - k);j <= min(a[i] + k,n);j++){
                    v[i - pos[j] + n].push_back(i);
                }
            }
            memset(dp, -1, sizeof(dp));
            printf("%d
    ",dfs(n,n));
        }
        return 0;
    }
    
    
  • 相关阅读:
    简单的javascript抽奖程序
    Linux 二层协议架构组织
    常用正则表达式总结
    很好的矩阵覆盖问题
    很好的求幂的题目
    不错的题目-n个数连接得到的最大值
    netstat命令介绍-要用熟
    一次完整的http事务
    Apache vs. Nginx
    Python学习-生成器
  • 原文地址:https://www.cnblogs.com/jiachinzhao/p/7294499.html
Copyright © 2011-2022 走看看