jzoj5919. 【NOIP2018模擬10.22】逛公園(tarjan,二分)
5919. 【NOIP2018模擬10.22】逛公園
Description
琥珀色黃昏像糖在很美的遠方,思念跟影子在傍晚一起被拉長……
Description
小 B 帶著 GF 去逛公園,公園一共有 n 個景點,標號為 1 . . . n。景點之間有 m 條路徑相連。
小 B 想選擇編號在一段區間 [l, r] 內的景點來遊玩,但是如果這些景點的誘導子圖形成了環,那麼 GF 將會不高興。
小 B 給出很多個詢問 [x, y],想讓你求有多少個區間 [l, r] 滿足 x ≤ l, r ≤ y 且不會使 GF不高興。
Input
第一行為兩個整數 n, m,表示景點和路徑的數量。
第 2 . . . m + 1 行每行兩個整數 ui, vi 表示第 i 路徑的兩端。
第 m + 2 行是一個整數 q 表示詢問的個數,接下來 m 行每行兩個整數 xi, yi 表示詢問。
Output
q 行,每行一個整數表示答案。
Sample Input
8 9
1 2
2 3
3 1
4 5
5 6
6 7
7 8
8 4
7 2
3
1 8
1 4
3 8
Sample Output
27
8
19
Data Constraint
對於 30% 的資料,n, m ≤ 100。
對於另外 10% 的資料,n = m + 1。
對於另外 10% 的資料,n = m
對於 100% 的資料,n, m ≤ 3 × 10^5, xi ≤ yi,不存在重邊、自環,不存在一條邊同時存在於兩個不同的簡單環。
Hint
誘導子圖:子圖 G′ = (V′, E′),原圖 G = (V, E)。V′ 是 V 的子集,E′ = {(u, v)|u, v ∈V′,(u, v) ∈ E}
分析:我們先 DFS 出圖的每一個環,得到環上編號最小和最大的節點,那麼合法的區間一定不
能同時跨過這兩個點。於是我們搞出一個 R 陣列,R[i] 表示以 i 作為左端點時右端點最右可
以到哪個位置。R 陣列顯然是單調遞增的。
對於詢問 l, r,我們二分出一個位置 i 滿足 R[l . . . i − 1] ≤ r,R[i . . . r] ≥ r,直接計算即可。
程式碼
#include <cstdio> #include <algorithm> #define N 500000 #define ll long long using namespace std; struct arr { int to,nxt,num; }a[N * 2]; struct cir { int l,r; }b[N],h[N]; int n,m,l,ls[N],f[N],can[N]; int vis[N],circle[N],tot; int dfn[N],low[N],s[N],cnt,top; ll sum[N]; bool fl; int cmp(cir x, cir y){return x.r < y.r;} void add(int x, int y, int i) { a[++l].to = y; a[l].nxt = ls[x]; a[l].num = i; ls[x] = l; } void tarjan(int u, int fa) { dfn[u] = low[u] = ++cnt; s[++top] = u; for (int i = ls[u]; i; i = a[i].nxt) if (a[i].to != fa) if (!dfn[a[i].to]) { tarjan(a[i].to, u); low[u] = min(low[u], low[a[i].to]); if (dfn[u] <= low[a[i].to]) { int minn = 1e9, maxx = 0, tmp = top; while (top) { int x = s[top--]; maxx = max(maxx, x); minn = min(minn, x); if (x == a[i].to) break; } maxx = max(maxx, s[top]); minn = min(minn, s[top]); if (tmp - top > 1) can[minn] = min(maxx - 1, can[minn]); } } else low[u] = min(low[u], dfn[a[i].to]); } int main() { freopen("graph4.in","r",stdin); // freopen("graph.out","w",stdout); scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); add(x, y, i); add(y, x, i); } for (int i = 1; i <= n; i++) can[i] = n; for (int i = 1; i <= n; i++) if (!dfn[i]) tarjan(i, 0); for (int i = n - 1; i >= 1; i--) can[i] = min(can[i], can[i + 1]); for (int i = 1; i <= n; i++) sum[i] = sum[i - 1] + (ll)(can[i] - i + 1); int q; scanf("%d", &q); while (q--) { int x, y; ll ans = 0; scanf("%d%d", &x, &y); int L = x, R = y, pos; while (L <= R) { int mid = (L + R) / 2; if (can[mid] >= y) pos = mid, R = mid - 1; else L = mid + 1; } ans = sum[pos - 1] - sum[x - 1]; ans += (ll)(2 + y - pos) * (y - pos + 1) / 2; printf("%lld\n", ans); } }