给出了一个\(N\)个点\(M\)条边的有向图
求有多少个有序点对\((a,b)\),满足至少存在一个点\(c\)以及从\(c\)到\(a\)的一条路径\(L_a\),\(c\)
到 \(b\) 的一条路径\(L_b\),使得\(L_a\)的长度是\(L_b\)的两倍
注意不一定是简单路径
\(N,M\leq 3\times10^3\)
考虑对于点\(c\),一个合法的二元组是怎样形成的
我们可以看成是由点\(a,b\)由\(c\)开始,点\(a\)走一步,点\(b\)走两步这样不断行走而形成的
我们把这个过程拆成三个阶段:
对于任意一组合法的方案我们进行上述操作,一定都能得到一组新的合法的方案
我们考虑记忆化搜索
令\(f[a][b][0]\)为合法状态,\(f[a][b][1]\)为第一阶段的状态,\(f[a][b][2]\)为第二阶段的状态。当到达第三阶段时,我们又获得一个合法状态,于是又用\(f[a][b][0]\)存储
考虑记忆化搜索的方式,我们用广搜不断扩展合法状态,类似今年省选\(\tt {D2T1}\)校园路径
最后统计答案累加即可
一共只有\(3N^2\)个状态,所以最后的时间复杂度是\(O(3N^2)\)的
#include <cstdio>
#include <queue>
using namespace std;
const int N = 3010;
int read();
struct node {
int u, v, w;
node(int _u, int _v, int _w) : u(_u), v(_v), w(_w) {}
};
int n, m;
int cap;
int head[N], nxt[N], to[N];
int f[N][N][3];
queue<node> que;
inline void add(int x, int y) {
to[++cap] = y, nxt[cap] = head[x], head[x] = cap;
}
int main() {
n = read(), m = read();
for (int i = 1; i <= m; ++i) {
int x = read(), y = read();
add(x, y);
}
for (int i = 1; i <= n; ++i) que.push(node(i, i, 0));
for (int i = 1; i <= n; ++i) f[i][i][0] = 1;
while (!que.empty()) {
node top = que.front(); que.pop();
int u = top.u, v = top.v, w = top.w;
if (!w) {
for (int i = head[u]; i; i = nxt[i])
if (!f[to[i]][v][1])
f[to[i]][v][1] = 1, que.push(node(to[i], v, 1));
} else {
w = (w + 1) % 3;
for (int i = head[v]; i; i = nxt[i])
if (!f[u][to[i]][w])
f[u][to[i]][w] = 1, que.push(node(u, to[i], w));
}
}
int ans = 0;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
ans += f[i][j][0];
printf("%d\n", ans);
return 0;
}
int read() {
int x = 0, c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x;
}
原文:https://www.cnblogs.com/VeniVidiVici/p/11537541.html