题目链接: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)。
请计算是否能使所有炸弹同时处于非激活态,如果可行,输出要切断的电缆。
(1leq Nleq 10^5)
(1leq Aileq 10^9),(A_i) 互不相同
(1leq Mleq 2*10^5)
(1leq L_jleq R_jleq 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)) 预处理点对距离然后二分图跑费用流的做法。