*题解:[AGC056C] 01 Balanced
解析
看到区间限制,考虑将其转化为前缀限制。将 \(0\) 看作 \(-1\),那么对于限制 \((L,R)\),其相当于限制 \(pre_R - pre_{L - 1} = 0\),其中 \(pre\) 为结果序列的前缀和。
考虑差分约束,由于结果序列中每一位要么是 \(1\) 要么是 \(-1\),所以前缀和序列每一位相比前一位都会变化 \(1\),即 \(\lvert pre_i - pre_{i - 1} \rvert = 1\)。这下就有个问题,这个约束实际上是 \(pre_i - pre_{i - 1} = 1\) 或 \(pre_{i - 1} - pre_i = 1\)。要跑差分约束只能是 \(pre_i - pre_{i - 1} \le 1\) 且 \(pre_{i - 1} - pre_i \le 1\),这样就会出现 \(pre_{i} = pre_{i - 1}\) 的情况......吗?由于题目给出了 \((R_i - L_i + 1) \equiv 0 \pmod 2\),即 \((R_i - L_i) \equiv 1 \pmod 2\) 的约束,所以每走一条 \(0\) 权边会跳过偶数个权为 \(-1\) 的边。也就是说,对于点 \(i\) 来说,从 \(0\) 到达它所经过的权为 \(-1\) 的边的个数 \(c_i\) 的奇偶性取决于其编号奇偶性,故 \(c_i\) 的奇偶性不同于 \(c_{i - 1}\) 的奇偶性,故 \(pre_i \not= pre_{i - 1}\)。
于是连从 \(i\) 到 \(i - 1\) 的边权为 \(-1\) 的双向边和从 \(L - 1\) 到 \(R\) 的边权为 \(0\) 的双向边,将 \(pre_0\) 设为 \(0\),从它开始后差分约束跑最长路即可。跑最长路是因为我们希望 \(s\) 的字典序最小,即 \(pre\) 的字典序最小。
由于边权只有 \(0\) 和 \(-1\),可以使用 01BFS。
时间复杂度 \(O(n + m)\)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 1e6 + 10,M = 300000 + 5,K = 10000 + 5;
vector<pii> g[N];
int pre[N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
// freopen("in.txt","r",stdin);
// freopen("out1.txt","w",stdout);
memset(pre,128,sizeof(pre));
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++){
int l,r;
cin>>l>>r;
g[l - 1].push_back({r,0});
g[r].push_back({l - 1,0});
}
for(int i=1;i<=n;i++){
g[i].push_back({i - 1,-1});
g[i - 1].push_back({i,-1});
}
deque<int> q;
pre[0] = 0;
q.push_back(0);
while(!q.empty()){
int u = q.front();
q.pop_front();
for(pii p : g[u]){
int v = p.first,w = p.second;
if(pre[v] < pre[u] + w){
pre[v] = pre[u] + w;
if(w == -1){
q.push_back(v);
}else{
q.push_front(v);
}
}
}
}
for(int i=1;i<=n;i++){
cout<<(pre[i] - pre[i - 1] == -1 ? 0 : 1);
}
return 0;
}
/*
*/

浙公网安备 33010602011771号