AtCoder ABC 155F Perils in Parallel
题目链接:https://atcoder.jp/contests/abc155/tasks/abc155_f
题目大意
有$N$个炸弹排成一列,放置在不同的坐标位置,其中部分激活了,部分没激活,有$M$个操控装置,每个操控装置能同时反转坐标区间$[L, R]$上炸弹的激活状态。问能否把所有炸弹调成未激活状态,没有则输出$-1$,有则输出需要需要操作的装置个数并升序输出装置序号,若答案不唯一,随意输出一个即可。
分析
把炸弹先按坐标位置从小到大排个序,然后把每个操控装置能同时反转的坐标区间转化为能同时反转的炸弹数组下标区间(这一步相当重要,数据范围直接降了下来),还是记为$[L, R]$,不过这里含义已经变了哦。
设排序后的第$i$个炸弹为$bomb[i]$。
对于操控装置的功能,其实等价于先把区间$[0, L - 1]$上炸弹的激活状态反转,再把区间$[0, R]$上炸弹的激活状态反转。为了统一操作,我们把区间右值往后挪一个,这样作用区间就变成$[L, R)$了。于是,操控装置的功能,就等价于先把区间$[0, L - 1]$上炸弹的激活状态反转,再把区间$[0, R - 1]$上炸弹的激活状态反转。为了对应改动,需要假想第$N + 1$个未激活的炸弹,对应下标为$N$。
设$D[i](0)$为使用第$i$个操作装置把区间$[0, L_i - 1]$上炸弹的激活状态反转。
设$D[i](1)$为使用第$i$个操作装置把区间$[0, R_i - 1]$上炸弹的激活状态反转。
那么,我们我们只需要同时地执行$D[i](0)$和$D[i](1)$操作即可。
再来看下$bomb[i - 1]$和$bomb[i]$,无非有激活状态相同和激活状态不同两种可能情况。
然后想一下我们的目的,把所有炸弹搞成未激活。
所有炸弹未激活等价于两两相邻的炸弹激活状态相同且最后一个炸弹($bomb[N]$)未激活。
设$state[i] = bomb[i - 1] \oplus bomb[i]$。
于是我们只需要同时地执行$D[i](0)$和$D[i](1)$操作,使得$state[i]$全为$0$即可(不存在全激活的可能,因为$bomb[N]$一定是未激活的)。
接下来中重头戏,构造图,以炸弹数组下标为顶点,操作区间为边构造DAG,于是问题转化为不重复地选择边,使得$state[i]$全为$0$。
当我们$D[i](0)$时,$state[L_i]$必然发生改变;当我们$D[i](1)$时,$state[R_i]$必然发生改变。
反之,当我们想要改变$state[X_i]$时,一定是通过$D[i](0)$或$D[i](1)$来实现。
也就是说,如果要改变顶点$i$的$state[i]$,则必须选中与此顶点相关的一条边,进而与此边相连的另一个顶点的$state$值也发生改变,不断影响下去直到$state[i]$全为$0$或者无法全为$0$。
可以用$DFS$来实现这个过程(具体可以见代码),如果最后还有节点的$state$值为$1$的话,就表明无法全部设置成未激活。
代码如下
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 /*-------------------Define Start-------------------*/ 5 typedef bool BL; // 布尔类型 6 typedef char SB; // 有符号1字节,8位 7 typedef unsigned char UB; // 无符号1字节,8位 8 typedef short SW; // 有符号短整型,16位 9 typedef unsigned short UW; // 无符号短整型,16位 10 typedef long SDW; // 有符号整型,32位 11 typedef unsigned long UDW; // 无符号整型,32位 12 typedef long long SLL; // 有符号长整型,64位 13 typedef unsigned long long ULL; // 无符号长整型,64位 14 typedef char CH; // 单个字符 15 typedef float R32; // 单精度浮点数 16 typedef double R64; // 双精度浮点数 17 18 #define Rep(i, n) for (register SDW i = 0; i < (n); ++i) 19 #define For(i, s, t) for (register SDW i = (s); i <= (t); ++i) 20 #define rFor(i, t, s) for (register SDW i = (t); i >= (s); --i) 21 #define foreach(i, c) for (__typeof(c.begin()) i = c.begin(); i != c.end(); ++i) 22 #define ms0(a) memset(a,0,sizeof(a)) 23 #define msI(a) memset(a,0x7f,sizeof(a)) 24 #define LOWBIT(x) ((x)&(-x)) 25 26 #define MP make_pair 27 #define PB push_back 28 #define ft first 29 #define sd second 30 #define ALL(x) x.begin(),x.end() 31 32 #define pr(x) cout << #x << " = " << x << " " 33 #define prln(x) cout << #x << " = " << x << endl 34 35 const ULL mod = 1e9 + 7; //常用模数(可根据题目需要修改) 36 const ULL inf = 0x7fffffff; //用来表示无限大 37 const ULL infLL = 0x7fffffffffffffffLL; //用来表示无限大 38 39 // 重载<<操作符,用来打印vector 40 template < typename T > 41 ostream& operator<< (ostream& out, vector< T > vec) { 42 foreach(i, vec) { 43 if(i != vec.begin()) { 44 out << " "; 45 } 46 out << *i; 47 } 48 return out; 49 } 50 /*-------------------Define End-------------------*/ 51 52 const UDW maxN = 1e5 + 7; 53 54 struct Bomb{ 55 SDW pos; // 炸弹的坐标 56 BL st; // 激活状态 57 58 Bomb() {} 59 Bomb(SDW pos, BL st) : pos(pos), st(st) {} 60 61 BL operator< (const Bomb &x) const { 62 return pos < x.pos; 63 } 64 }; 65 66 struct Vertex{ 67 vector< SDW > next; 68 }; 69 70 struct Edge{ 71 SDW id, from, to; 72 73 Edge(SDW id, SDW from, SDW to) : id(id), from(from), to(to) {} 74 }; 75 76 SDW N, M; 77 Bomb bomb[maxN]; 78 vector< Edge > E; 79 Vertex V[maxN]; 80 BL vis[maxN]; 81 SDW k; 82 vector< SDW > c; 83 84 void addEdge(Edge x) { 85 V[x.from].next.PB(E.size()); 86 E.PB(x); 87 } 88 89 void input(){ 90 cin >> N >> M; 91 Rep(i, N) { 92 cin >> bomb[i].pos >> bomb[i].st; 93 } 94 sort(bomb, bomb + N); // 把炸弹按坐标排序,设bomb[N]为假想的炸弹 95 96 Rep(i, M) { 97 SDW L, R; 98 cin >> L >> R; 99 // 把坐标覆盖区域[L, R]转化为炸弹下标覆盖区域[u, v),如此一来顶点有V[0]~V[N]一共N+1个 100 SDW u = lower_bound(bomb, bomb + N, Bomb(L, false)) - bomb; // u为覆盖区域[L, R]中离L最近的炸弹在bomb数组中的位置 101 SDW v = upper_bound(bomb, bomb + N, Bomb(R, false)) - bomb; // v为覆盖区域[L, R]中离R最近的炸弹在bomb数组中的位置的后一个位置 102 if(u != v) { 103 addEdge(Edge(i << 1, u, v)); 104 addEdge(Edge(i << 1 | 1, v, u)); 105 } 106 } 107 } 108 109 BL dfs(SDW x) { 110 vis[x] = true; 111 112 foreach(i, V[x].next) { 113 Edge e = E[*i]; 114 115 // 如果dfs(e.to)为真,则代表边e需要被选一次 116 if(!vis[e.to] && dfs(e.to)) { 117 c.PB((e.id >> 1) + 1); 118 ++k; 119 // 当边e被选时,以下两个值会发生变化 120 bomb[x].st ^= 1; 121 bomb[e.to].st ^= 1; // 可以不要这条语句,但是写上,便于理解 122 } 123 } 124 125 return bomb[x].st; 126 } 127 128 void solve(){ 129 rFor(i, N, 1) { 130 bomb[i].st ^= bomb[i - 1].st; 131 } 132 133 For(i, 0, N) { 134 if(!vis[i] && dfs(i)) { 135 k = -1; 136 break; 137 } 138 } 139 } 140 141 void output(){ 142 cout << k << endl; 143 if(k != -1) { 144 sort(ALL(c)); 145 cout << c << endl; 146 } 147 } 148 149 int main() { 150 input(); 151 solve(); 152 output(); 153 return 0; 154 }