1688F
set,2500
题意
给出 a , b a,b a,b 两个序列,并给出 m m m 个区间 [ l i , r i ] [l_i,r_i] [li,ri],每次可以从它们中满足 ∑ i = l r a i = ∑ i = l r b i \sum_{i=l}^ra_i=\sum_{i=l}^rb_i ∑i=lrai=∑i=lrbi 的区间中任选一个,对这个区间的所有数执行 a i = b i a_i=b_i ai=bi,问能否经过若干次操作,使得 a a a 序列和 b b b 序列相同?
思路
区间可以被选当且仅当这个区间内两序列和相同,也就是差为0,所以用数组 s i s_i si 表示两个数组前缀和之差,即 s i = s i − 1 + a i − b i s_i=s_{i-1} + a_i - b_i si=si−1+ai−bi,则区间 [ l , r ] [l,r] [l,r] 可以被选当且仅当 s l − 1 = s r s_{l-1}=s_r sl−1=sr,且操作过后 s l − 1 = s l = . . . = s r s_{l-1}=s_l=...=s_r sl−1=sl=...=sr,最终目标是令 s s s 数组全为 0 0 0 ,因此选 s l − 1 ≠ 0 s_{l-1}\neq 0 sl−1=0 的没有意义。
所以题可以转化为:对于区间 [ l , r ] [l,r] [l,r] ,如果 s l − 1 = s r = 0 s_{l-1}=s_{r}=0 sl−1=sr=0 ,则使 s l − 1 = s l = . . . = s r = 0 s_{l-1}=s_l=...=s_r=0 sl−1=sl=...=sr=0 , 是否可以使 s s s 全为 0 0 0 。
可以用 queue
存数组
s
s
s 已经为0的位置,set
存还不是0的位置,依次遍历已经为0的位置所连的边,若另一点也为0,则遍历范围在这个区间内的 set
中元素并加入队列。如果最终队空但 set
未空,则无解。每个点至多加入队列一次,时间复杂度
O
(
(
n
+
m
)
log
n
)
O((n+m)\log n)
O((n+m)logn)
代码
int n, m;
int a[maxn], b[maxn], s[maxn];
vector<int> e[maxn];
void solve() {
cin >> n >> m;
set<int> se;
for(int i = 1; i <= n; i++) {
cin >> a[i];
se.insert(i);
}
for(int i = 1; i <= n; i++) {
cin >> b[i];
s[i] = s[i-1] + a[i] - b[i];
e[i].clear();
}
e[0].clear();
//se 表示非0的位置
for(int i = 1; i <= m; i++) {
int x, y;
cin >> x >> y;
x--;
e[x].pb(y);
e[y].pb(x);
}
queue<int> q;
for(int i = 0; i <= n; i++) {
if(!s[i]) {
q.push(i);
se.erase(i);
}
}
while(!q.empty()) {
int x = q.front();
q.pop();
for(int i = 0; i < e[x].size(); i++) {
int y = e[x][i];
if(s[y]) continue;
int l = min(x, y), r = max(x, y);
auto itl = se.lower_bound(l), itr = se.upper_bound(r);
for(auto it = itl; it != itr; it++) {
s[*it] = 0;
q.push(*it);
}
se.erase(itl, itr);
}
}
if(!se.empty()) {
cout << "NO\n";
}
else {
cout << "YES\n";
}
}