一道好題,感覺解法非常自然。
首先我們只需要考慮一次染色最下面被包含的那些區間,因為把無解判掉以後只要染了乙個節點,它的祖先也一定被染了。然後發現一次染色最下面的那些區間一定是一段連續的左兒子+一段連續的右兒子。
證明的話可以看官方題解,感性理解的話不難,同時,任意一段連續的左兒子+右兒子也對應乙個區間。定義乙個左兒子區間 \([l_i,r_i]\) 的後繼是所有 \(r_i=l_i+1\) 的左兒子和右兒子,乙個右兒子區間 \([l_i,r_i]\) 的後繼是所有 \(r_i=l_i+1\) 的右兒子區間,不難發現這是乙個dag。那麼這張圖的一條路徑就對應了原圖的乙個染色區間,也就是要求這個dag的最小路徑覆蓋,優化建圖+上下界最小流即可。
code
/*program by mangoyang*/
#include #define inf (0x7f7f7f7f)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template inline void read(t &x)
const int n = 200005, m = 200005;
int l[n], r[n], col[n], low[n], isl[n], n, ns, nt, cnt = 1;
inline void init(int u, int l, int r)
low[u] = col[u] && (!col[lc]) && (!col[rc]);
}namespace flow
inline int bfs()
} return ~dis[t];
} inline int dfs(int u, int flow)
return used;
} inline void setflow(int x, int y)
inline int getflow()
}inline void addedge(int x, int y, int a, int b)
int main()
else if(r[i] < n)
addedge(i + (n << 1), r[i] + 1 + (n << 2), 0, inf);
} flow::setflow(ns, nt);
flow::getflow();
flow::addedge(t, s, inf);
cout << flow::getflow() << endl;
return 0;
}
線段樹 單調棧 UNR 1 爭奪聖杯
用單調棧求出left right 可以發現每個點的貢獻是關於left right的分段函式 然後就是分段函式累加求和 當時打的線段樹 看了題解漲姿勢了 可以差分 做到o n include include includeusing namespace std typedef pairabcd typ...
UOJ 217 UNR 1 奇怪的線段樹
網路流 我們先來推一波性質 1 無解當且僅當乙個節點為0且它的兒子為1,有解的話,只有深度極大的節點是有用的 訪問它祖先一定也訪問了 2 任何乙個區間定位都是一段連續的右兒子 一段連續的左兒子 3 性質2的逆定理成立 實際上,每個右兒子區間左端點一定不同,左兒子區間右端點不同 這意味著每個右兒子後繼...
JZOJ5456 奇怪的佇列 線段樹
題目 給出nn n個人的身高,第i ii個人記得他的前面或者後面有a i a i a i 個人比他高。這些人字典序最小的排列。40分做法 先將所有人的身高排序,然後dfs dfsdf s全排列,再判斷是否成立。時間複雜度 o n n2 o n n 2 o n n2 100分做法 按照樣例一來模擬 4...