cf1688 F. Sanae and Giant Robot

题意:

给定长都为 n 的数组 \(a[],b[]\),并给定 m 个区间。

每次操作可以从给定区间里选一个区间 \([l,r]\),要求 \(\sum\limits _l^r a_i=\sum\limits _l^r b_i\),然后令 $a_i\gets b_i(l\le i\le r) $

问经过任意次操作后能否使 \(a[]\) 变成 \(b[]\)

\(n,m\le 2e5\)

思路:

\(c_i=a_i-b_i\),那么要求就变为 $ \sum\limits _l^r c_i = 0$

记 $sum[] $ 为 \(c[]\) 的前缀和,那么要求就变为 \(sum_{l-1}=sum_r\),操作就变为令 \(sum_{l\sim r}\gets sum_{l-1}\)

目标是使所有 \(sum_i=0\)

所以只能找 \(sum_{l-1}=sum_r=0\) 的区间,令 \(sum_{l\sim r}\gets 0\)。找端点不为 0 的区间是在做无用功

写法是用 queue 记录所有 0 位置进行搜索,每次从 queue 中取一值作为区间的一个端点,若其对应的另一端点也为 0 就把两端点间的值置 0

检查另一端点是不是 0 的方法是开 set 维护所有非 0 位置。用 set 而不是 map 主要是为了快速进行置 0 操作,即只把非 0 位置置 0

const signed N = 5 + 2e5;
int n, m, a[N], b[N];
void sol() {
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    for(int i = 1; i <= n; i++) cin >> b[i];

    vector<ll> sum(n+1); //ai-bi的前缀和
    for(int i = 1; i <= n; i++)
        sum[i] = sum[i-1] + a[i] - b[i];

    vector<vector<int>> seg(n+1); //区间的另一端点
    while(m--) {
        int l, r; cin >> l >> r;
        seg[l-1].pb(r), seg[r].pb(l-1);
    }

    set<int> non_zero; //非0位置
    for(int i = 0; i <= n; i++)
        if(sum[i]) non_zero.insert(i);

    queue<int> zero; //sum=0的位置
    for(int i = 0; i <= n; i++)
        if(!sum[i]) zero.push(i);

    while(zero.size()) {
        int u = zero.front(); zero.pop();
        for(int v : seg[u]) if(!non_zero.count(v)) {
            auto [l, r] = minmax(u, v);
            auto it = non_zero.upper_bound(l); //把[l,r]置0
            while(it != non_zero.end() && *it < r)
                zero.push(*it), it = non_zero.erase(it);
        }
    }
    cout << (non_zero.empty() ? "YES\n" : "NO\n");
}
posted @ 2022-07-18 11:11  Bellala  阅读(41)  评论(0编辑  收藏  举报