• [CF1442B] Identify the Operations


    Description

    给定两个数组 (a,b),长度分别为 (n,m( m le n))(a)(1..n) 的全排列,(b) 是一个 (1..n)(m) 排列。(b) 是目标序列,即我们需要通过以下的操作来构造一个答案序列使其等于 (b)。每次操作选择一个 (t),将 (a_{t-1})(a_{t+1}) 插入答案序列末尾并将 (a_t) 删除。问有多少种不同的构造方案。

    Solution

    考虑对于出现在 (b) 中的每一个 (a_i),我们必然要删除 (a_{i-1})(a_{i+1}) 中的至少一个。

    (f(a_i)) 表示 (a_i)(b) 中的出现位置。

    如果 (a_{i-1}) 存在,并且 (f(a_{i-1}) le f(a_{i})),那么我们称这个 (a_{i-1}) 对于 (a_i) 是合法的。(a_{i+1}) 同理。

    对于 (a_i),有如下三种情况:

    • 如果 (a_{i-1},a_{i+1}) 均合法,那么我们可以删除任意一个,方案数乘 (2)。之所以现在可以删除任意,是因为 (a_{i-1},a_i,a_{i+1}) 的出现位置必然都在 (b) 中后面所有数之前,因此他们并没有区别。

    • 如果 (a_{i-1},a_{i+1}) 中恰好有一个合法,那么我们把它删除即可。

    • 如果 (a_{i-1},a_{i+1}) 均不合法,则无解。

    #include <bits/stdc++.h>
    using namespace std;
    
    #define int long long
    const int mod = 998244353;
    const int N = 1000005;
    
    struct Node
    {
        int val;
        Node *prec = NULL;
        Node *succ = NULL;
    } node[N];
    
    void deleteNode(Node *p)
    {
        if (p == NULL)
            return;
        Node *prec = p->prec;
        Node *succ = p->succ;
        p->prec = p->succ = NULL;
        if (prec)
            prec->succ = succ;
        if (succ)
            succ->prec = prec;
    }
    
    void deleteNode(Node &p)
    {
        deleteNode(&p);
    }
    
    int n, m, a[N], b[N], f[N], c[N];
    
    void clearall(int n)
    {
        for (int i = 0; i <= n+1; i++)
            a[i] = b[i] = f[i] = c[i] = node[i].val = 0,
            node[i].prec = node[i].succ = 0;
    }
    
    void solve()
    {
        cin >> n >> m;
    
        clearall(n);
    
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        for (int i = 1; i <= m; i++)
            cin >> b[i];
        for (int i = 1; i <= m; i++)
            f[b[i]] = i;
        for (int i = 1; i <= n; i++)
            node[i].val = a[i];
        for (int i = 1; i <= n; i++)
            node[i].prec = node[i].succ = NULL;
        for (int i = 1; i < n; i++)
            node[i].succ = &node[i + 1];
        for (int i = 2; i <= n; i++)
            node[i].prec = &node[i - 1];
        for (int i = 1; i <= n; i++)
            c[a[i]] = i;
        int ans = 1;
        for (int j = 1; j <= m; j++)
        {
            int i = c[b[j]];
            Node *p = &node[i];
            Node *prec = p->prec;
            Node *succ = p->succ;
            if (prec && f[prec->val] > j)
                prec = NULL;
            if (succ && f[succ->val] > j)
                succ = NULL;
            if (prec && succ)
            {
                ans *= 2;
                ans %= mod;
                deleteNode(prec);
            }
            else if (prec)
            {
                deleteNode(prec);
            }
            else if (succ)
            {
                deleteNode(succ);
            }
            else
            {
                cout << 0 << endl;
                return;
            }
        }
        cout << ans << endl;
    }
    
    signed main()
    {
        ios::sync_with_stdio(false);
    
        int t;
        cin >> t;
        while (t--)
        {
            solve();
        }
    }
    
  • 相关阅读:
    flutter item列表左右滑动带出按钮选项
    flutter之Dismissible组件
    mac下最好用的抓包工具--charles简单操作教程
    flutter 开起charles抓包代理
    nodejs querystring模块
    node.js 中使用 Mongodb
    MongoDB 的高级查询 aggregate 聚合管道
    Mongodb 关系型数据库表(集合)与表(集合)之 间的几种关系
    Mongodb 的使用以及 Mongodb 账户权 限配置
    MongoDb 大数据查询优化、 MongoDB 索引、复合索引、唯一索引、 explain 分 析查询速度
  • 原文地址:https://www.cnblogs.com/mollnn/p/13955256.html
Copyright © 2020-2023  润新知