#796 Div.2 F. Sanae and Giant Robot set *

46 篇文章 1 订阅
43 篇文章 0 订阅
该博客讨论了一种算法问题,涉及两个序列a和b,以及一系列区间操作。问题在于通过选择满足序列和相等的区间,是否能将序列a转换为序列b。文章介绍了思路,使用前缀和差数组s,以及队列和集合来判断和更新可选区间,最终判断是否能将所有s[i]设为0以实现序列同步。若队列为空但集合不空,则无法达到目标。
摘要由CSDN通过智能技术生成

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=si1+aibi,则区间 [ l , r ] [l,r] [l,r] 可以被选当且仅当 s l − 1 = s r s_{l-1}=s_r sl1=sr,且操作过后 s l − 1 = s l = . . . = s r s_{l-1}=s_l=...=s_r sl1=sl=...=sr,最终目标是令 s s s 数组全为 0 0 0 ,因此选 s l − 1 ≠ 0 s_{l-1}\neq 0 sl1=0 的没有意义。

所以题可以转化为:对于区间 [ l , r ] [l,r] [l,r] ,如果 s l − 1 = s r = 0 s_{l-1}=s_{r}=0 sl1=sr=0 ,则使 s l − 1 = s l = . . . = s r = 0 s_{l-1}=s_l=...=s_r=0 sl1=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";
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值