Codeforces 429E - Points and Segments(歐拉回路)
果然我不具備融會貫通的能力/ll
看到這樣的設問我們可以很自然地聯想到這道題,具體來說我們可以通過某種方式建出一張圖,然後根據”每個點度都是偶數的圖必然每個連通塊都存在歐拉回路“這一條件構造出原圖的歐拉回路進而求解答案。因此現在問題轉化為如何構建出這樣一張圖出來。
首先一個非常直觀的想法是對於每個區間新建一個左部點,對於數軸上每一個整點新建一個右部點,然後從每個區間表示的左部點向這段區間中所有整點表示的右部點連邊,這樣問題可以變為,對於每個右部點,與其相連的左部點中紅點與藍點的差的絕對值 \(\le 1\),不過注意到這樣一來涉及與每個點相連的點 instead of 邊,這是不好直接用歐拉回路求解的,因此我們必須遲早放棄這個想法(
不難發現,如果我們將所有染成紅色的視作 \(+1\),染成藍色視作 \(-1\),那麼一個點符合 \(|r(x)-b(x)|\le 1\) 的要求當且僅當將所有覆蓋它的所有線段權值之和為 \(-1,0\) 或 \(1\)。因此問題可以轉化為,有 \(n\) 個區間,你要選擇對於每一個區間,選擇將區間中所有位置上的數 \(+1\) 或者 \(-1\),使得最終每個位置上的數的絕對值 \(\le 1\)。我們不妨先考慮一個弱化版,也就是所有點被覆蓋次數恰好為偶數的情況,此時最終序列中每個數都應是 \(0\)。注意到這裡涉及區間操作,而區間操作可以視作差分序列上的兩個端點操作,也就是說對於一個區間 \([l,r]\)
注意,由於區間長度很大,需要離散化。具體來說按照 P3643 [APIO2016]划艇 的套路,將所有區間改寫成一個左閉右開的區間,然後離散化一下即可將整個數軸拆成 \(\mathcal O(n)\) 個左閉右開的區間,那麼顯然每一個左閉右開的區間中所有點最終的權值都是一樣的,因此我們只用取這個區間最左邊的點作為該區間的代表點即可。
時間複雜度 \(\mathcal O(n\log n)\),因為要離散化。
const int MAXN=1e5;
int n,l[MAXN+5],r[MAXN+5],key[MAXN*2+5],cnt=0,uni[MAXN*2+5],num=0;
int d[MAXN*2+5],hd[MAXN*2+5],nxt[MAXN*6+5],to[MAXN*6+5],ec=1;
void adde(int u,int v){/*printf("adde %d %d\n",u,v);*/to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dir[MAXN*3+5],vis[MAXN*2+5],now[MAXN*2+5];
void dfs(int x){
// printf("dfs %d\n",x);
vis[x]=1;
for(int &e=now[x];e;e=nxt[e]) if(!~dir[e>>1]){
dir[e>>1]=e&1;dfs(to[e]);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&l[i],&r[i]);++r[i];
key[++cnt]=l[i];key[++cnt]=r[i];
} sort(key+1,key+cnt+1);key[0]=-1;
for(int i=1;i<=cnt;i++) if(key[i]^key[i-1]) uni[++num]=key[i];
for(int i=1;i<=n;i++){
l[i]=lower_bound(uni+1,uni+num+1,l[i])-uni;
r[i]=lower_bound(uni+1,uni+num+1,r[i])-uni;
d[l[i]]++;d[r[i]]--;adde(l[i],r[i]);adde(r[i],l[i]);
}
for(int i=1;i<=num;i++){
d[i]+=d[i-1];
if(d[i]&1) adde(i,i+1),adde(i+1,i);
}
memset(dir,-1,sizeof(dir));
for(int i=1;i<=num;i++) now[i]=hd[i];
for(int i=1;i<=num;i++) if(!vis[i]) dfs(i);
for(int i=1;i<=n;i++) printf("%d%c",dir[i]," \n"[i==n]);
return 0;
}