交换

tag:

  • cf分段1800+
  • 字典树,哈希hash,枚举

题意:

给定连续操作序列,对于操作序列l,r,使其实现交换当前位置上的数字
给定一堆未完成的排列,利用连续操作序列使其变成顺序递增排列

题解:

这题最先通过连续操作序列这一点就能想到o(n2)o(n^2)的枚举状态获得对应操作结果。

那么如果我们可以记录下所有操作对应的结果,然后通过逆向操作就能知道当前序列最后操作的结果是什么情况。

比如说我们操作 3 2 1,我们的一个连续操作序列可以使得原串 1 2 3 变成 3 2 1 ,那么他就可以使得原串为 3 2 1的序列变成 1 2 3

然后就是查询的问题 ,根据某沙老师所言,最初他并没有想卡掉map的查询,而是后面某人想到了利用字典树实现查询加速的方案,才使得这题变得如此妖孽。

字典树的查询时间跟串长有关,这题的串长只有10,所以时间复杂度相当低。而map的log跟字符串数量有关,至少是1e6起步,所以会很慢。(也就是俗称的卡常)

当然这里也给一些别的我看到的思路,比如说康托展开加速,或者玄学hash并放在数组里o1查询,这些都是一些比较可行的办法(但并不常见)

代码

#include<bits/stdc++.h>
using namespace std;

int n, m;
int x[20];

pair<int, int>rm[2040];
#define ll long long
vector<array<int,10>> nex(1);
#include<bits/stdc++.h>
using namespace std;
struct trie {
    
    int ve[20000004];
    int cnt = 0;
    //s是字符串,l是s的长度
    void insert(string s, int l,int num) {
        int p = 0;
        for (int i = 0; i < l; i++) {
            int c = s[i] - '0';
            if (!nex[p][c]) nex.push_back({}),nex[p][c]=nex.size()-1;
            p = nex[p][c];
        }
        //代表他是否出现过
        if(ve[p])
       ve[p] = min(num,ve[p]);
        else ve[p] = num;
    }
    int find(string s, int l) {
        int p = 0;
        for (int i = 0; i < l; i++) {
            int c = s[i] - '0';
            if (!nex[p][c])return 0;
            p = nex[p][c];
        }
        return ve[p];
    }
}tr;

int main() {
    int n, m;
    ios::sync_with_stdio(0);
    cin >> n >> m;
    int a, b;
    map<string, int>mp;
    for (int i = 1; i <= n; i++) {
        cin >> rm[i].first >> rm[i].second;
    }
    for (int l = 1; l <= n; l++) {
        string ans = "0123456789";
        for (int j = l; j <= n; j++) {
            swap(ans[rm[j].first - 1], ans[rm[j].second - 1]);
            string tmp;
            for (int k = 0; k < 10; k++) {
                tmp += ans[k];
                tr.insert(tmp,tmp.size(),j - l + 1);
            }
        }
    }
    for (int i = 1; i <= m; i++) {
        cin >> n;
        string pp;
        int bb[10] = {};
        int f = 0;
        for (int j = 1; j <= n; j++) {
            int tt;
            cin >> tt;
            if (tt != j)f = 1;
            bb[tt] = j - 1;
        }
        if (f == 0) {

            cout << "0\n";
            continue;
        }
        for (int j = 1; j <= n; j++) {
            pp += char(bb[j] + '0');
        }

        if (tr.find(pp,pp.size()) == 0)cout << -1 << "\n";
        else cout << tr.find(pp,pp.size()) << "\n";
    }
}