zhao_ry514114
赵若伊

导航

 

思路

每次操作都是:

  • 把值为 \(a_q\) 的数从当前排列中删除;
  • 再把它放到排列末尾。

考虑一个数如果被操作了很多次,那么前面的操作其实都会被后面的操作覆盖。

例如一个数在第 \(2\) 次和第 \(5\) 次操作中都出现,那么最终它的位置只由第 \(5\) 次操作决定。

所以对于所有出现过的数,我们只需要关心它们的最后一次出现位置。

最终排列分成两部分

最终排列可以拆成两部分:

  1. 从来没有被操作过的数;
  2. 被操作过的数。

对于第一部分,因为它们从来没有被删除,所以它们之间的相对顺序始终不会改变。

因此只需要按照原排列 \(P\) 的顺序输出所有没有被操作过的数即可。

对于第二部分,它们最终会被依次移动到末尾。

如果两个数 \(x,y\) 的最后一次出现位置满足 \(last_x<last_y\),那么 \(x\) 最后一次被移到末尾发生得更早,所以最终 \(x\) 一定在 \(y\) 前面。

因此,被操作过的数应该按照它们最后一次出现的先后顺序排列。

如何求最后一次出现顺序

可以从后往前扫描操作序列 \(a\)。

如果当前的 \(a_i\) 之前没有出现过,就说明当前位置 \(i\) 正是它的最后一次出现位置。

于是把它加入 ans。

因为我们是从后往前扫描,所以得到的顺序正好是最后出现时间从晚到早。

最后把 ans 翻转一次,就变成最后出现时间从早到晚。

代码中:

per (i, q, 1) {
    if (!u[a[i]]) {
        u[a[i]] = true;
        ans.eb (a[i]);
    }
}
reverse (ans.begin (), ans.end ());

其中 u[x] 同时表示值 \(x\) 是否在操作中出现过。

最后输出

先按照原排列顺序输出所有没有被操作过的数:

rep (i, 1, n)
    if (!u[p[i]]) cout << p[i] << ' ';

再输出按照最后一次出现顺序排列的所有被操作过的数:

for (int i : ans)
    cout << i << ' ';

这样就得到了最终排列。

如何想到的

直接模拟删除和插入会比较麻烦,所以可以先考虑一个数被操作多次时会发生什么。

比如一个数先被移动到末尾,之后又再次被移动到末尾,那么第一次移动显然已经没有意义了。

因此自然想到,每个数其实只需要保留它的最后一次操作。

这样问题就变成:

  • 没被操作过的数保持原相对顺序;
  • 被操作过的数按照最后一次操作的时间排序。

而想得到“最后一次出现”,最方便的方法就是从后往前扫描,第一次遇到某个数时记录下来即可。

代码

#include <bits/stdc++.h>
#define rep(i,j,k) for (int i = (j); i <= (k); i ++)
#define per(i,j,k) for (int i = (j); i >= (k); i --)
#define uint unsigned int
#define ll long long
#define ull unsigned long long
#define db double
#define ldb long double
#define mkp make_pair
#define eb emplace_back
#define e_ emplace
#define ls (p << 1)
#define rs (p << 1 | 1)
//#define int ll
using namespace std;
const int N = 2e5 + 5;
//const int MOD = 998244353;
//const int MOD = 1000000007;
const int INF = 0x3f3f3f3f;
const ll LINF = 4e18;
//int T;
int n, q;
int p[N], a[N];
bool u[N];
signed main () {
    //freopen (".in", "r", stdin);
    //freopen (".out", "w", stdout);
    ios::sync_with_stdio (0);
    cin.tie (0);
    cin >> n >> q;
    rep (i, 1, n) cin >> p[i];
    rep (i, 1, q) cin >> a[i];
    vector<int> ans;
    per (i, q, 1) {
        if (! u[a[i]]) {
            u[a[i]] = true;
            ans.eb (a[i]);
        }
    }
    reverse (ans.begin (), ans.end ());
    rep (i, 1, n) if (!u[p[i]]) cout << p[i] << ' ';
    for (int i : ans) cout << i << ' ';
    return 0;
}

复杂度

操作序列扫描一次,原排列再扫描一次。

时间复杂度为 \(O(N+Q)\)。

posted on 2026-09-11 19:12  zhao_ry514114  阅读(16)  评论(0)    收藏  举报