知识点:拓扑排序,位运算,bitset
这个题才刚刚写了,虽然是拓扑排序的例题,但是其实就是对位运算和bitset的练习题,做了这么多的题,才第一次明确遇见集合的概念
至于为什么是集合,我觉得最基本的原因是这个题是一个图,所以可以用集合(虽然还没学过离散数学,尤其是集合论与图论),对每个点,我们都用一个集合来表示这个点能到达的点,很显然拓扑排序最后的点只能到自己,倒数第二个点能到自己或者最后一个点,如果有边的话,那么这个题我们可以按照拓扑排序的逆序,从后向前推,每到一个点,求自身,与所有出边相连的点的集合求并集,那么就是当前点的可以到达的点的集合,最后输出每个点的集合的大小就行了,
然后是关键,怎么来表示集合,可以用位运算,比如说用二进制的1101,来表示集合里面有1,3,4,但是没有2,求集合的并,就用二进制的并运算就行了,但是还有一个问题,这个题的点的个数是3e4,不像别的搜索的题目,很少,10个20个,就用一个int就行了,这里显然不行,我们就按一个int表示30位,那么一个点需要1000个int,我一开始就是这样子写的,但是最后一个点超时了,然后看了看发现bitset是真的强,上面用了1000个int来表示30000个数,bitset用一个就行了,而且修改,求并,查询里面1的个数等等操作,真的是很方便,这个stl真的很强感觉
然后就是对于状态压缩的理解,之前做过两个BFS题,那个是把10维的数组给压缩成1维了,这里其实是和数独问题一样,把可供选择的选项,或者是集合里面的元素,若干个数,压缩成一个,以达到降低时间的目的,就是这里不知道可供选择的选项,或者集合里面的元素,是不是也可以称之为状态,两者都很巧妙但是还是稍有不同的
这是之前的超时的代码,但是我觉得对位运算的理解是有帮助的
#include <bits/stdc++.h>
using namespace std;
const int N = 3e4 + 5;
int n, m, a[N][1005], ind[N], b[N], cnt, c[N];
int tot, ver[N], head[N], nxt[N];
void add(int x, int y) {
ver[++tot] = y;
nxt[tot] = head[x]; head[x] = tot;
}
void toposort() {
queue<int> q;
for (int i = 1; i <= n; i++) {
if (!ind[i]) q.push(i);
}
while (!q.empty()) {
int now = q.front(); q.pop();
b[++cnt] = now;
for (int i = head[now]; i; i = nxt[i]) {
int y = ver[i];
if (--ind[y] == 0) q.push(y);
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y);
ind[y]++;
}
toposort();
reverse(b + 1, b + n + 1);
for (int i = 1; i <= n; i++) {
int x = b[i];
a[x][(x - 1) / 31] |= (1 << ((x - 1) % 31));
for (int j = head[x]; j; j = nxt[j]) {
int y = ver[j];
for (int k = 0; k < 1000; k++) {
a[x][k] |= a[y][k];
}
}
for (int j = 0; j < 1000; j++) {
for (int k = a[x][j]; k; k -= (k & -k)) {
c[x]++;
}
}
}
for (int i = 1; i <= n; i++) {
printf("%d\n", c[i]);
}
return 0;
}
之后是bitset的,这个stl第一次用,觉得真的很强
#include <bits/stdc++.h>
using namespace std;
const int N = 3e4 + 5;
int n, m, ind[N], b[N], cnt, c[N];
int tot, ver[N], head[N], nxt[N];
bitset<N> a[N];
void add(int x, int y) {
ver[++tot] = y;
nxt[tot] = head[x]; head[x] = tot;
}
void toposort() {
queue<int> q;
for (int i = 1; i <= n; i++) {
if (!ind[i]) q.push(i);
}
while (!q.empty()) {
int now = q.front(); q.pop();
b[++cnt] = now;
for (int i = head[now]; i; i = nxt[i]) {
int y = ver[i];
if (--ind[y] == 0) q.push(y);
}
}
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
add(x, y);
ind[y]++;
}
toposort();
reverse(b + 1, b + n + 1);
for (int i = 1; i <= n; i++) {
int x = b[i];
a[x][x] = 1;
for (int j = head[x]; j; j = nxt[j]) {
a[x] |= a[ver[j]];
}
}
for (int i = 1; i <= n; i++) {
printf("%d", a[i].count());
}
return 0;
}