CF1687C
令 \(c_i=a_i-b_i\)。
那么原题操作变成若区间 \([l,r]\) 的 \(c\) 和为 \(0\),那么可以将这段区间的 \(c\) 都变成 \(0\)。
最终目标是 \(c\) 全部变成 \(0\)。
令 \(sum_i\) 表示 \(c\) 的前缀和。
此时操作变成,若 \(sum_{l-1}=sum_r\),可以将这段区间的 \(sum\) 都变成 \(sum_{l-1}\)。
最终目标是 \(sum\) 全部变成 \(0\)。
不难发现若 \(sum_{l-1}=sum_r\ne0\),执行这个操作是不优的,于是应该只在 \(sum_{l-1}=sum_r=0\) 的时候执行操作。
于是接下来就是每次找一对 \(sum_{l-1}=sum_r=0\),然后将这段区间的 \(sum\) 全部置 \(0\),直到不能操作为止。
最终如果 \(sum\) 全变成 \(0\) 了就输出 YES
,否则是 NO
。
但是暴力 BFS 扩展时间复杂度是 \(\mathcal O(nm)\) 的,考虑优化这个过程。
可以用 set
维护当前非 \(0\) 的位置,每次将当前更新的区间中的非 \(0\) 位置全部取出来放进队列里继续 BFS,然后在 set
中删去这些点。
不难发现这样复杂度是 \(\mathcal O((n+m)\log n)\) 的。
Code:
#include <bits/stdc++.h>
using namespace std;
#define YES return printf("YES\n"), void()
#define NO return printf("NO\n"), void()
#define lb lower_bound
typedef long long ll;
const int N = 200005;
int T;
int n, m;
int a[N], b[N], diff[N]; ll sum[N];
vector <int> g[N];
set <int> S;
queue <int> q;
void upd(int l, int r) {
if (l > r) swap(l, r);
for (auto it = S.lb(l); it != S.end() && (*it) <= r; it = S.lb(l)) q.push(*it), S.erase(it);
}
void solve() {
scanf("%d%d", &n, &m);
S.clear();
for (int i = 0; i <= n; ++i) g[i].clear();
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i <= n; ++i) scanf("%d", &b[i]), diff[i] = b[i] - a[i], sum[i] = sum[i - 1] + diff[i];
for (int i = 1, l, r; i <= m; ++i) scanf("%d%d", &l, &r), g[l - 1].push_back(r), g[r].push_back(l - 1);
if (sum[n]) NO;
for (int i = 0; i <= n; ++i) if (sum[i]) S.insert(i); else q.push(i);
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : g[u]) if (!S.count(v)) upd(u, v);
}
if (!S.empty()) NO;
YES;
}
int main() {
scanf("%d", &T);
while (T--) solve();
return 0;
}