Atcoder ABC155 F - Perils in Parallel
题目链接:ABC155 F - Perils in Parallel
题意:
一个国家中被放了 \(N\) 颗炸弹,序号为 \(1\) 到 \(N\),第 \(i\) 个炸弹位于坐标 \(A_i\),若 \(B_i=1\),它目前处于激活态,反之则不处于激活态。这个炸弹系统有序号为 \(1\) 到 \(M\) 的 \(M\) 根电缆,若我们切断电缆 \(j\),则位于 \([L_j,R_j]\) 的炸弹状态会切换,\(0\)->\(1\) 或 \(1\)->\(0\)。
请计算是否能使所有炸弹同时处于非激活态,如果可行,输出要切断的电缆。
\(1\leq N\leq 10^5\)
\(1\leq Ai\leq 10^9\),\(A_i\) 互不相同
\(1\leq M\leq 2*10^5\)
\(1\leq L_j\leq R_j\leq 10^9\)
思路:
首先将每个区间操作差分化,切断一根电缆只会改变两个地方的异或值,我们将相邻两个元素的异或看成一个节点,把电缆切断后改变的两个点之间连边,得到一张无向图,由于选一条路径时我们只关注端点的情况(中间的点变了两次,等于没变),而且是想让异或值为1的点两两配对,可以发现环不环是不重要的。
对所有连通块 \(DFS\)(随便一棵树其实都行),考虑如何配对,如果子树内异或值为1的点数量为偶数,则应该已经配好了,若为奇数,我们需要向外帮没配对的点找对象,就把现在这条边选上,如果没有配对,则边会一直连出去,直到点数为偶数,此时肯定是配上了。
如果发现整个连通块异或值为1的点数量是奇数,则无解。
时间复杂度 \(O(NlogN)\) ,瓶颈在于给坐标排序。这题算是 \(ABC\) 中很有难度的一题了,一开始没做出来,主要是没想到无向图可以直接转化成树。
Code:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#define mem(a,b) memset(a,b,sizeof(a))
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define per(i,b,a) for(int i=b;i>=a;i--)
#define PII pair<int,int>
#define mk make_pair
#define fr first
#define sc second
#define N 100100
#define M 200201
using namespace std;
int head[N],to[M*2],nxt[M*2];
int cnt,id[2*M],siz[N];
bool parity[N],vis[N];
int loc[N],l[M],r[M],n,m;
PII ab[N];
vector<int> ans;
void init(){mem(head,-1),cnt=-1;}
void add_e(int a,int b,int k){
nxt[++cnt]=head[a],head[a]=cnt,to[cnt]=b,id[cnt]=k;
}
bool cmp(PII a,PII b){return a.fr<b.fr;}
void dfs(int x){
siz[x]=parity[x];
vis[x]=true;
for(int i=head[x];~i;i=nxt[i]){
if(vis[to[i]])continue;
dfs(to[i]);
if(siz[to[i]]&1)ans.push_back(id[i]);
siz[x]+=siz[to[i]];
}
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
rep(i,1,n){
cin>>ab[i].fr>>ab[i].sc;
loc[i]=ab[i].fr;
}
sort(ab+1,ab+n+1,cmp);
sort(loc+1,loc+n+1);
rep(i,1,n) parity[i]=ab[i].sc^ab[i-1].sc;
parity[n+1]=ab[n].sc;
init();
rep(i,1,m){
cin>>l[i]>>r[i];
int L=lower_bound(loc+1,loc+n+1,l[i])-loc;
int R=upper_bound(loc+1,loc+n+1,r[i])-loc;
if(L==R)continue;
add_e(L,R,i),add_e(R,L,i);
}
rep(i,1,n+1)if(!vis[i]){
dfs(i);
if(siz[i]&1){puts("-1");return 0;}
}
cout<<ans.size()<<endl;
sort(ans.begin(),ans.end());
rep(i,0,(int)ans.size()-1)cout<<ans[i]<<" ";
cout<<endl;
return 0;
}
想了一下如果题目的切电缆是有代价的怎么做,好像只会 \(O(N^3)\) 预处理点对距离然后二分图跑费用流的做法。