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)\) 预处理点对距离然后二分图跑费用流的做法。

posted @ 2021-03-02 20:40  Neal_lee  阅读(111)  评论(0编辑  收藏  举报