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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhanghaoxian1/article/details/83279493

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

猜你喜欢

转载自blog.csdn.net/zhanghaoxian1/article/details/83279493