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(nk\log 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\n",dfs(n,n));
    }
    return 0;
}

posted @ 2017-08-06 13:11  jiachinzhao  阅读(211)  评论(0编辑  收藏  举报