1. 程式人生 > >jzoj5919. 【NOIP2018模擬10.22】逛公園(tarjan,二分)

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);
	}
}