【题解】CodeForces 2163 C Monopati

题目链接

CodeForces 2163 C Monopati

题目大意

给定一个 \(2\)\(n\) 列的数组 \(a\),每个元素 \(a_{i,j}\) 均为整数且属于 \([1, 2n]\) , 对于给定的 \(1 \leq l \leq r \leq 2n\) , 构造数组 \(b\)\(l \leq a_{i,j} \leq r\) 则, \(b_{i,j} = 1\) 否则\(b_{i,j} = 0\), 若存在一条仅向右或向下走的路径使得 \((1, 1)\) 能够到达 \((2, n)\) , 且经过的每一个 \(b_{i,j}\) 均为 1 ,则称 这一对给定的 \((l, r)\) 合法。

问有多少组合法的 \((l, r)\)

思路

经过简单观察,我们可以发现如下规律:

  1. 如果 \((l, r)\) 合法,\((l, r+1)\) 一定合法,如果 \((l, r)\) 不合法, \((l+1, r)\) 一定不合法。
  2. 对于给定 \(l\) ,我们称最小的合法 \(r\)\(r_l\), 左边界为 \(l\) 的所有合法方案数为 \(2n+1 - r_l\) ,为方便后续处理,我们记当无论如何都无法得到合法方案时 \(r_l=2n+1\),即调整合法方案为0。
  3. 对于一个确定的 \((l, r)\) 我们记第一行所有出现的 \(0\) 的位置的集合为 \(l_1\) , 第二行所有出现的 \(0\) 的位置的集合为 \(l_2\) , 当 \(min(l_1) > max(l_2) + 1\) 时,答案合法。这个结论正向不容易想到, \(min(l_1)\) 第一行出现的第一个 0 的位置,之前全是1, \(max(l_2)\) 是第二行出现的最后一个 0 的位置,之后全是1,如果 \(min(l_1) > max(l_2) + 1\) , 则存在合法解。
  4. \((l, r)\) 转移至 \((l, r+1)\) ,只有 \(\{b_{i,j}| a_{i,j} = r+1\}\) 中的元素可能发生变化,其余部分不变, 从 \((l, r)\) 转移至 \((l+1, r)\) 同理。

显然,这是一个双指针法,然后我们用两个set维护两行中出现的0即可。

如何维护呢?

  1. 输入时开 \(2n\) 个vector记录,第 \(k\) 个vector存 \(\{(i,j) | a_{i,j} = k\}\) , 我的代码中分第一第二行两个vector避免出现pair。
  2. 初始状态,所有元素都是0,加入 \(l_1\) \(l_2\),我的代码中还加入了两个哨兵节点避免后续处理set空的情况。
  3. \((0, 0)\) 开始
    \((l, r)\) 转移至 \((l, r+1)\)时,把 \(\{b_{i,j}| a_{i,j} = r+1\}\) 中的所有 \(b_{i,j}\) erase 掉,重复执行直到存在合法解,此时得到的 \(r\) 即为 \(r_l\)
    \((l, r)\) 转移至 \((l+1, r)\)时,把 \(\{b_{i,j}| a_{i,j} = l+1\}\) 中的所有 \(b_{i,j}\) 重新insert,重复执行直到解不合法,期间不断在 \(ans\) 上累加 \(2n+1 - r\)
    检查退出循环

然后这题就完成了。

AC代码

\(ans\) 最好开 \(long \ long\),没试过,但是可能会炸。

可以统计出现的最小最大值来优化常数。

#include <iostream>
#include <vector>
#include <set>
using namespace std;

const int N = 200005;
int a[2][N];
vector<int> inv[2][2*N];

set<int > l1, l2;
int n;

bool check() {
    if(*l1.begin()>*l2.rbegin()+1) {
        return true;
    } else {
        return false;
    }
}


int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    int T;
    cin>>T;
    while(T--) {
        cin>>n;
        int mini = 0x3f3f3f3f;
        int maxi = 0;
        l1.clear();
        l2.clear();
        for(int i=1;i<=2*n;i++) {
            inv[0][i].clear();
            inv[1][i].clear();
        }
        for(int i=1;i<=n;i++) {
            cin>>a[0][i];
            maxi = max(maxi, a[0][i]);
            mini = min(mini, a[0][i]);
            inv[0][a[0][i]].push_back(i);
        }
        for(int i=1;i<=n;i++) {
            cin>>a[1][i];
            maxi = max(maxi, a[1][i]);
            mini = min(mini, a[1][i]);
            inv[1][a[1][i]].push_back(i);

            l1.insert(i);
            l2.insert(i);
        }

        l1.insert(n+1);
        l2.insert(0);

        int l=1, r = mini-1; // l不合法下界,r合法下界
        long long ans = 0;
        while(true) {
            while(r<maxi && !check()) {
                ++r;
                for(int i:inv[0][r]) {
                    l1.erase(i);
                }
                for(int i:inv[1][r]) {
                    l2.erase(i);
                }
            }

            if(!check()) {
                break;
            }

            while(l<=a[0][1] && check()) {
                ans += 2*n-r+1;
                for(int i:inv[0][l]) {
                    l1.insert(i);
                }
                for(int i:inv[1][l]) {
                    l2.insert(i);
                }
                l++;
            }
        }
        cout<<ans<<endl;
    }
}

单组数据时间复杂度 \(O(n log n)\) ,每个元素最多被erase并insert一次,每次处理会由于set带来不大于 \(log n\) 的复杂度。

posted @ 2025-11-11 18:54  MistryNihilityn  阅读(44)  评论(0)    收藏  举报