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 }
View Code
原文地址:https://www.cnblogs.com/zaq19970105/p/12340031.html