Just Add an Edge

Just Add an Edge

一道妙题,考虑加入边\((x, y)\)是合法的,那么原图一定能分成两条的不交的路径且覆盖了\(n\)个点,一条是以\(x\)为终点的,一条是以\(y\)为起点的。
将属于以\(x\)为终点的路的点标为\(0\),属于\(y\)的标为\(0\),那么第一个 \(1\) 一定是\(y\),最后一个 \(0\) 一定是\(x\),且对于\(i < y\)\(cor_i = 0\),对于\(i > x\)\(cor_i = 1\)
我们可以用\(0,1\)改变的位置来唯一确定一种\(0/1\)序列,所以设\(f_{i, 0/1}\)表示填到第\(i\)个位置,\(0/1\)表示\(i\)\(0/1\),且强制\(i-1\)\(i\)的标号不同,这样做是\(O(nm)\)
发现对于第一个位置\(i\)满足\(i\)\(i + 1\)不相连,\(i\)\(i + 1\)标号一定不同,那么\(DP\)一定会经过\(f_{i,0/1}\),所以可以将\([1,i + 1]\)\([i+1,n]\)\(f\)值都用\(f_{i, 0/1}\)表示,时间复杂度\(O(n + m)\)

Code
#include<cstdio>
#include<iostream>
#include<vector>
#define eb emplace_back
#define IN inline
#define LL long long
using namespace std;
const int N = 150005;
int T, n, m, bz[N], nxt[N], f[N][2]; LL c[4], d[4];
vector<int> E[N];

IN int read() {
	int t = 0,res = 0; char ch = getchar();
	for (; !isdigit(ch); ch = getchar()) t |= (ch == '-');
	for (; isdigit(ch); ch = getchar()) res = (res << 3) + (res << 1) + (ch ^ 48);
	return t ? -res : res;
}
int main() {
	T = read();
	for (; T; T--) {
		n = read(), m = read(), E[n + 1].clear(), bz[n] = 0;
		for (int i = 2; i <= n; i++) E[i].clear(), E[i].eb(0);
		for (int i = 1; i < n; i++) E[n + 1].eb(i), bz[i] = 0;
		for (int i = 1; i <= m; i++) {
			int q = read(), p = read();
			if (q + 1 == p) bz[q] = 1;
			else E[p].eb(q);
		}
		int L = 0, R = 0;
		for (int i = 1; i < n; i++) R = (!bz[i] ? i + 1 : R);
		for (int i = n; i >= 1; i--) L = (!bz[i] ? i : L);
		for (int i = n; i >= 1; i--) nxt[i] = bz[i] ? nxt[i + 1] : i;
		if (L == n) {
			printf("%lld\n", (LL)n * (n - 1LL) / 2LL); continue;
		}
		for (int i = 0; i <= n + 1; i++) f[i][0] = f[i][1] = 0;
		f[L + 1][0] = 1, f[L + 1][1] = 2;
		for (int i = L + 2; i <= n + 1; i++)
			for (auto v : E[i])
				for (int k = 0; k < 2; k++)
					f[i][k] |= (nxt[v + 1] >= i - 1 ? f[v + 1][k ^ 1] : 0);
		for (int i = L + 1; i >= 1; i--)
			for (auto v : E[i])
				for (int k = 0; k < 2; k++)
					f[v + 1][k ^ 1] |= (nxt[v + 1] >= i - 1 ? f[i][k] : 0);
		for (int i = 0; i < 4; i++) c[i] = d[i] = 0;
		for (int i = 1; i <= L + 1; i++) c[f[i][1]]++;
		for (int i = R; i <= n + 1; i++) d[f[i][1]]++;
		LL ans = 0;
		for (int i = 0; i < 4; i++)
			for (int j = 0; j < 4; j++)
				ans += ((i & j) ? c[i] * d[j] : 0LL); 
		printf("%lld\n", ans - (L + 1 == R));
	} 
}

posted @ 2023-03-15 21:58  RiverSheep  阅读(26)  评论(0编辑  收藏  举报