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;
}
posted @ 2022-11-12 22:21  Kobe303  阅读(21)  评论(0编辑  收藏  举报