【CF429E】Points and Segments
题目
题目链接:https://codeforces.com/problemset/problem/429/E
有若干区间 \([l_i,r_i]\),你需要把每一个区间染上黑色或白色,使得最后每一个数字被染成黑色次数与被染成白色次数的差的绝对值不超过 \(1\)。
\(n\leq 10^5\)。
思路
先把坐标离散化,考虑把所有区间扔到数组上,黑色为 \(1\),白色为 \(-1\),那么需要合理赋值颜色使得每一个点的值都是 \(-1,0,1\) 中的一个。
但是这样依然不好处理,发现如果一个点被偶数个区间覆盖,那么它最终必然为 \(0\),否则必然为 \(±1\)。那么我们可以把每一个被奇数个区间覆盖的点再加上一个长度为 \(1\) 的区间覆盖它,然后问题转化为要求所有点的数值都变为 \(0\)。我们并不需要关心这个长度为 \(1\) 的区间的颜色,因为如果是黑色表示原本这个点的数值为 \(1\),白色表示原本为 \(-1\)。
我们再把一个区间 $[l_i,r_i] $看作一条 \(l_i\to r_i+1\) 的边,这样转化为一张图之后,所有点数值为 \(0\) 就等价于“正向经过一条边为 \(1\),逆向经过为 \(-1\),任意点的所连接的边的权值之和为 \(0\)”。
由于我们已经保证了所有点都被偶数个区间覆盖,也就是所有点的度数都为偶数,所以这张图必然存在欧拉回路,直接求出欧拉回路即可。
时间复杂度 \(O(n\log n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=400010;
int n,tot=1,l[N],r[N],b[N],deg[N],head[N];
bool vis[N];
struct edge
{
int next,to,tag;
}e[N];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to,0};
head[from]=tot;
}
void dfs(int x)
{
vis[x]=1;
for (int i=head[x];~i;i=e[i].next)
if (!e[i].tag)
{
e[i].tag=1; e[i^1].tag=2;
dfs(e[i].to);
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d",&n);
for (int i=1;i<=n;i++)
{
scanf("%d%d",&l[i],&r[i]);
r[i]++; b[i*2]=l[i]; b[i*2-1]=r[i];
}
sort(b+1,b+1+2*n);
int m=unique(b+1,b+1+2*n)-b-1;
for (int i=1;i<=n;i++)
{
l[i]=lower_bound(b+1,b+1+m,l[i])-b;
r[i]=lower_bound(b+1,b+1+m,r[i])-b;
add(l[i],r[i]); add(r[i],l[i]);
deg[l[i]]++; deg[r[i]]++;
}
for (int i=1;i<=m;i++)
if (deg[i]&1)
{
add(i,i+1); add(i+1,i);
deg[i]++; deg[i+1]++;
}
for (int i=1;i<=m;i++)
if (!vis[i]) dfs(i);
for (int i=1;i<=n;i++)
printf("%d ",e[i*2].tag-1);
return 0;
}