Codeforces Round 848 (Div. 2) B. The Forbidden Permutation(贪心)

The Forbidden Permutation

给你一个长度为 n 的排列 p 、一个由 m 组成的数组 a 1 , a 2 , … , a m ( 1 ≤ a i ≤ n ) a_1,a_2,…,a_m ( 1≤a_i≤n ) a1,a2,,am(1ain)和一个整数 d 组成的数组。不同的整数 a 1 , a 2 , … , a m ( 1 ≤ a i ≤ n ) a_1,a_2,…,a_m ( 1≤a_i≤n ) a1,a2,,am(1ain),以及整数 d 。

p o s ( x ) pos(x) pos(x) x x x 在排列 p p p 中的索引。如果出现以下情况,那么数组 a a a 就不好。

p o s ( a i ) < p o s ( a i + 1 ) ≤ p o s ( a i ) + d pos(a_i)<pos(a_{i+1})≤pos(a_i)+d pos(ai)<pos(ai+1)pos(ai)+d 为所有的 1 ≤ i < m 1≤i<m 1i<m .
例如,排列 p=[4,2,1,3,6,5] 和 d=2 :

a=[2,3,6] 不是一个好数组。a=[2,6,5] 是好数组,因为 p o s ( a 1 ) = 2 , p o s ( a 2 ) = 5 pos(a_1)=2 , pos(a_2)=5 pos(a1)=2pos(a2)=5 ,所以不满足条件 p o s ( a 2 ) ≤ p o s ( a 1 ) + d pos(a_2)≤pos(a_1)+d pos(a2)pos(a1)+d

a=[1,6,3] 是好数组,因为 p o s ( a 2 ) = 5 , p o s ( a 3 ) = 4 pos(a_2)=5 , pos(a_3)=4 pos(a2)=5pos(a3)=4 ,所以不满足条件 p o s ( a 2 ) < p o s ( a 3 ) pos(a_2)<pos(a_3) pos(a2)<pos(a3)

在一步棋中,你可以交换排列组合 p 中相邻的两个元素。要使数组 a 变为好数组所需的最少步数是多少?可以证明总是存在一个移动序列使得数组 a 变为好数组。

排列数组是由 n 个不同的整数组成的数组,这些整数从 1 到 n 按任意顺序排列。例如, [2,3,1,5,4] 是一个排列,但 [1,2,2] 不是一个排列( 2 在数组中出现了两次), [1,3,4] 也不是一个排列( n=3 ,但数组中有 4 )。

输入

每个测试包含多个测试用例。第一行包含测试用例的数量 t ( 1 ≤ t ≤ 1 0 4 ) t ( 1≤t≤10^4 ) t(1t104)。测试用例说明如下。

每个测试用例的第一行包含三个整数 n 、 m n 、 m nm d ( 2 ≤ n ≤ 1 0 5 、 2 ≤ m ≤ n 、 1 ≤ d ≤ n ) d ( 2≤n≤10^5 、 2≤m≤n 、 1≤d≤n ) d(2n1052mn1dn)、排列长度 p p p 、数组长度 a a a 和值 d d d

第二行包含 n 个整数 p 1 , p 2 , … , p n ( 1 ≤ p i ≤ n 、 p i ≠ p j 为 i ≠ j ) p_1,p_2,…,p_n ( 1≤p_i≤n 、 p_i≠p_j 为 i≠j ) p1,p2,,pn(1pinpi=pji=j)

第三行包含 m 个独立整数 a 1 , a 2 , … , a m ( 1 ≤ a i ≤ n , a i ≠ a j 为 i ≠ j ) a_1,a_2,…,a_m ( 1≤a_i≤n , a_i≠a_j 为 i≠j ) a1,a2,,am(1ainai=aji=j)

所有测试用例中 n 的总和不超过 5 ⋅ 1 0 5 5⋅10^5 5105

输出

对于每个测试用例,打印使数组 a a a 变为好数组所需的最小移动次数。

样例 #1

样例输入 #1

5
4 2 2
1 2 3 4
1 3
5 2 4
5 4 3 2 1
5 2
5 3 3
3 4 1 5 2
3 1 2
2 2 1
1 2
2 1
6 2 4
1 2 3 4 5 6
2 5

样例输出 #1

1
3
2
0
2

读题很重要
首先,这道题的条件是:
只要存在任何两个元素,使得满足 p o s ( a 1 ) + d < p o s ( a 2 ) pos(a_1) + d < pos(a_2) pos(a1)+d<pos(a2) 或者 p o s ( a 1 ) > p o s ( a 2 ) pos(a_1) > pos(a_2) pos(a1)>pos(a2),那么就可以说这是一个好数组。
并且应理解pos数组的含义,pos数组是p数组的元素的索引,所以我们可以先预处理出来pos数组以便于后面的操作。

可以将情况划分成两种:

  • 第一种:这个数组已经是好数组。即满足了pos[a[i+1]] <= pos[a[i]] || pos[a[i+1]] > d + pos[a[i]],那么直接将答案置为 0 0 0
  • 第二种:这个数组不是一个好数组。那么就需要考虑如何使用最少的步数将其变成好数组。

如何将不好的数组变为好数组 ?

  • 第一种做法:可以想办法使得存在 p o s ( a i ) > p o s ( a i + 1 ) pos(a_i) > pos(a_{i+1}) pos(ai)>pos(ai+1),那么最优的办法就是让 p o s ( a i + 1 ) pos(a_{i+1}) pos(ai+1) 交换到 p o s ( a i ) pos(a_i) pos(ai) 的前面去,这个方法会花费 p o s ( a i + 1 ) − p o s ( a i ) pos(a_{i+1}) - pos(a_i) pos(ai+1)pos(ai) 的步数。
  • 第二种做法:可以想办法使得存在 p o s ( a i + 1 ) > d + p o s ( a i ) pos(a_{i+1}) > d + pos(a_i) pos(ai+1)>d+pos(ai),在这里可以给这个式子变个形: p o s ( a i + 1 ) − p o s ( a i ) > d pos(a_{i+1}) - pos(a_i) >d pos(ai+1)pos(ai)>d 这样就变成了让这两个数的差值大于d,那么就可以让 p o s ( a i + 1 ) pos(a_{i+1}) pos(ai+1) 往右移动,然后让 p o s ( a i ) pos(a_{i}) pos(ai) 往左移动,这样消耗的步数就是 d − ( p o s ( a i + 1 ) − p o s ( a i ) ) + 1 d - (pos(a_{i+1}) - pos(a_{i})) + 1 d(pos(ai+1)pos(ai))+1。但是对于这种情况我们需要判断这两个元素是否能够移动的开,如果两个元素都超出边界了都无法满足条件,这个操作就无法成立。

CODE:

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+10;

int p[N];
int a[N];
int pos[N];
int n,m,d;

void solve(){
    cin >> n >> m >> d;
    for(int i = 1;i <= n;i++){
        cin >> p[i];
        pos[p[i]] = i; 
    }
    for(int i = 1;i <= m;i++)cin >> a[i];

    int ans = 2e9;

    for(int i = 1;i < m;i++){
        if(pos[a[i+1]] <= pos[a[i]] || pos[a[i+1]] > d + pos[a[i]]){
            ans = 0;        //如果存在任何pos(a1) + d < pos(a2)或者pos(a1) > pos(a2)
            break;          //那么这个数组就已经是一个好数组了
        }

        int dis = pos[a[i+1]] - pos[a[i]];  //第一种情况消耗的步数
        ans = min(ans,dis);

        //判断能否移动得开
        //  pos[a[i]] - 1表示从左边界到pos[a[i]]的距离
        //  n - pos[a[i+1]]表示从有边界到pos[a[i+1]]的距离
        //  d - dis + 1表示使得满足pos(a2) - pos(a1) > d所需要的距离
        //  (需要两者相差的距离 - 两者当前的距离)
        if((pos[a[i]] - 1 + n - pos[a[i+1]]) >= d - dis + 1){
            ans = min(ans,d - dis + 1);
        }
    }   
    cout << ans <<endl;
}

int main(){
    int T;cin >> T;
    while(T--){
        solve();
    }
    return 0;
}

相关推荐

  1. Codeforces Round 766 (Div. 2) (博弈论 + 贪心

    2024-05-13 12:32:07       31 阅读
  2. Codeforces Round 898 (Div. 4)

    2024-05-13 12:32:07       50 阅读
  3. Codeforces Round 777 (Div. 2) (C D分类讨论 E倍增+贪心)

    2024-05-13 12:32:07       54 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-05-13 12:32:07       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-05-13 12:32:07       100 阅读
  3. 在Django里面运行非项目文件

    2024-05-13 12:32:07       82 阅读
  4. Python语言-面向对象

    2024-05-13 12:32:07       91 阅读

热门阅读

  1. [力扣题解]45. 跳跃游戏 II

    2024-05-13 12:32:07       30 阅读
  2. Redis——Redis的数据库结构、删除策略及淘汰策略

    2024-05-13 12:32:07       34 阅读
  3. Tauri框架:使用Rust构建轻量级桌面应用

    2024-05-13 12:32:07       31 阅读
  4. C语言和BASH SHELL中条件表达式的真假与0和1的关系

    2024-05-13 12:32:07       31 阅读
  5. 运维:CentOS常见命令详解

    2024-05-13 12:32:07       30 阅读
  6. 蓝桥杯-错误票据(两种写法stringstream和扣字符)

    2024-05-13 12:32:07       37 阅读
  7. Spring常见的注解

    2024-05-13 12:32:07       33 阅读
  8. WPS加载项(wps jsapi)创建、发布及部署

    2024-05-13 12:32:07       36 阅读
  9. phpstorm环境配置与应用

    2024-05-13 12:32:07       30 阅读
  10. 汇编语言定义宏指令--.macro

    2024-05-13 12:32:07       30 阅读