jzoj5919. 【NOIP2018模拟10.22】逛公园(tarjan,二分)

xiaoxiao2022-05-17  3


某平台价值19860元的编程课程资料免费领取【点我领取】


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); } }
转载请注明原文地址: https://www.6miu.com/read-4884251.html

最新回复(0)