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