题意:有向图,问是否存在从 aabb 长度为 LL 的路径。

状压dp+bitset+倍增优化

取中间点 kk,若存在 iikk 长度为 pp ,从 kkjj 长度为 qq ,则状态转移得到存在 iijj 长度为 p+qp+q 的路径。

所以 dp[i][j][k]{0,1}dp[i][j][k]\in \{0,1\} 表示从 iikk 是否存在长度为 jj 的路径。但是显然这是存不下的,所以把 jj 变为存在长度为 2j2^j 的路径倍增。之所以能这样优化与本题边长都为 11 有关。由于每个 ll 都能被拆为几个二进制的和,所以最终查询时对 ll 再处理。

状态转移方程为

dp[i][j+1]=dp[k][j],(dp[i][j][k]=1)dp[i][j+1]|=dp[k][j],(dp[i][j][k]=1)

查询时由低到高遍历 ll,设当前能到点集为 SS,当前距离为 L1L_1,则距离为 L1+(1<<i)L_1+(1<<i) 能到的点集为 uSdp[u][i]\bigcup_{u\in S}dp[u][i]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<bits/stdc++.h>
using namespace std;
#define ll long long
typedef pair<int, int> pii;
const int INF = 0x3f3f3f3f;
const int maxn = 1e6 + 10;
int n, m, Q;
bitset<110>dp[110][40], ans;
int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
int u, v;
cin >> u >> v;
dp[u][0][v] = 1;
}
for (int j = 0; j < 30; j++)
for (int i = 1; i <= n; i++)
for (int k = 1; k <= n; k++)
if (dp[i][j][k])
dp[i][j + 1] |= dp[k][j];
cin >> Q;
while (Q--) {
int l, u, v;
ans.reset();
cin >> l >> u >> v;
ans[u] = 1;
for (int i = 0; i < 30; i++) {
if (l >> i & 1) {
bitset<110>tmp;
for (int j = 1; j <= n; j++)
if (ans[j]) tmp |= dp[j][i];
ans = tmp;
}
}
puts(ans[v] ? "YES" : "NO");
}
return 0;
}