CF 901C Bipartite Segments

link

题解

没有偶环,则原图为仙人掌,一个简单环可以对应到有且仅有一个点双。
二分图的充要条件即没有奇环,则 ([x,y]) 中不存在二分图的条件为不存在环。
则求一遍点双之后,对于每一个询问 ([l,r]),设 (nxt_i)([i,r]) 中以 (i)(x) 时,(y) 的最大值。
先考虑如何求 (nxt_i)。对于每一个简单环,设环内最小结点编号为 (mn),最大结点编号为 (mx),则令 (nxt_{mn} gets min { nxt_{mn}, mx })。然后从 (r)(l) 求后缀最小值,即 (nxt_i gets min { nxt_i, nxt_{i+1} })
然后考虑如何算贡献。对于 (nxt_i leq r),以 (i)(x) 时,对 ([x,y]) 方案数的贡献为 (nxt_i - i)。对于 (nxt_i > r),以 (i)(x) 时,对 ([x,y]) 方案数的贡献为 (r - i + 1)
显然 (nxt_i) 是单调的,那么可以二分求出最大的 (i) 满足 (nxt_i leq r)。对于第一种情况,用一个前缀和预处理。对于第二种情况,显然贡献为一个等差数列,直接算即可。
注意二分边界!!!

#include <cstdio>
#include <cctype>
#include <algorithm>
#define MAX_N (300000 + 5)
#define MAX_M (300000 + 5)
using std::max;
using std::min;
int n, m, q;
int h[MAX_N], tot;
struct Edge {
	int to, next;
} e[MAX_M * 2];
int dfn[MAX_N], low[MAX_N], cnt;
int st[MAX_N], tp;
int nxt[MAX_N];
long long sum[MAX_N];
void Read(int &x) {
	x = 0;
	char ch = getchar();
	while (!isdigit(ch)) ch = getchar();
	while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
}
void Write(long long x) {
	if (x >= 10) Write(x / 10);
	putchar(x % 10 + '0');
}
void AddEdge(int u, int v) {
	e[++tot].to = v;
	e[tot].next = h[u];
	h[u] = tot;
}
void Tarjan(int u) {
	dfn[u] = low[u] = ++cnt;
	st[++tp] = u;
	int v;
	for (int i = h[u]; i; i = e[i].next) {
		v = e[i].to;
		if (!dfn[v]) {
			Tarjan(v);
			low[u] = min(low[u], low[v]);
			if (dfn[u] <= low[v]) {
				int mn = u, mx = u, tmp = 1;
				while (st[tp] != u) {
					++tmp;
					mn = min(mn, st[tp]);
					mx = max(mx, st[tp]);
					--tp;
				}
				if (tmp >= 3) nxt[mn] = min(nxt[mn], mx);
			}
		}
		else low[u] = min(low[u], dfn[v]);
	}
}
int main() {
	Read(n), Read(m);
	int u, v;
	for (int i = 1; i <= m; ++i) Read(u), Read(v), AddEdge(u, v), AddEdge(v, u);
	for (int i = 1; i <= n; ++i) nxt[i] = n + 1;
	for (int i = 1; i <= n; ++i) if (!dfn[i]) Tarjan(i);
	for (int i = n - 1; i; --i)	nxt[i] = min(nxt[i + 1], nxt[i]);
	for (int i = 1; i <= n; ++i) sum[i] = sum[i - 1] + nxt[i] - i;
	Read(q);
	int ql, qr, l, r, mid;
	long long ans;
	while (q--) {
		Read(ql), Read(qr);
		l = ql - 1, r = qr;
		while (l < r) {
			mid = l + r + 1 >> 1;
			if (nxt[mid] > qr) r = mid - 1;
			else l = mid;
		}
		ans = sum[r] - sum[ql - 1] + (long long)(qr - r + 1) * (qr - r) / 2;
		Write(ans), putchar('
');
	}
	return 0;
}
原文地址:https://www.cnblogs.com/kcn999/p/14264435.html