【tarjan】P2272 [ZJOI2007]最大半连通子图
发布日期:2021-05-06 16:51:20 浏览次数:23 分类:精选文章

本文共 2930 字,大约阅读时间需要 9 分钟。

优化方案数计算的方法

在处理某类问题时,可能会遇到需要计算方案数的情况。为了高效解决这个问题,可以采用以下优化方法:

1. 缩点处理

首先,可以使用Tarjan算法进行缩点操作。这一步骤不会影响最终的答案结果,但可以大幅简化后续处理流程。通过缩点,可以将图中的强连通分量分解,使得后续的处理变得更加容易。

2. 拓扑排序

在完成缩点后,按照拓扑序对图进行处理。拓扑排序可以确保我们在处理节点时,总是先处理其所有依赖项,这对于动态规划等依赖性强的算法非常重要。

3. 动态规划求解方案数

由于拓扑排序已经确保了处理顺序的正确性,我们可以使用动态规划的方法来计算方案数。每个节点的状态可以基于其子节点的状态来更新,从而逐步计算出最终的方案总数。

4. 代码实现

以下是相关代码的实现说明:

#include 
using namespace std;
const int maxn = 1e5 + 5;
const int maxm = 1e6 + 5;
int n, m, times, tot, head[maxn], dfn[maxn], low[maxn];
long long mod, in[maxn];
struct edge {
int to, nxt;
} e[maxm];
void add(int x, int y) {
e[++tot].nxt = head[x];
e[tot].to = y;
head[x] = tot;
}
int col_num, top, inin[maxn], col[maxn], a[maxm], b[maxm], sta[maxn], sz[maxn];
void tarjan(int x) {
dfn[x] = low[x] = ++times;
sta[++top] = x;
inin[x] = true;
for (int i = head[x]; i; i = e[i].nxt) {
int y = e[i].to;
if (!dfn[y]) {
tarjan(y);
low[x] = min(low[x], low[y]);
} else if (inin[y]) {
low[x] = min(low[x], dfn[y]);
}
}
if (dfn[x] == low[x]) {
int y;
++col_num;
do {
y = sta[--top];
inin[y] = false;
col[y] = col_num;
sz[col_num]++;
} while (y != x);
}
}
void build() {
memset(head, 0, sizeof(head));
map
st;
for (int i = 1; i <= m; ++i) {
long long has = (a[i] * 100000 + b[i]);
if ((a[i] != b[i]) && !st[has]) {
add(a[i], b[i]);
st[has] = 1;
}
}
}
long long maxx, sum;
void toposort() {
memset(in, 0, sizeof(in));
for (int i = col_num; i >= 1; --i) {
if (!dis[i]) {
dis[i] = sz[i];
in[i] = 1;
}
for (int j = head[i]; j; j = e[j].nxt) {
int k = e[j].to;
if (dis[k] < dis[i] + sz[k]) {
dis[k] = dis[i] + sz[k];
in[k] = in[i];
} else if (dis[k] == dis[i] + sz[k]) {
in[k] = (in[k] + in[i]) % mod;
}
}
}
for (int i = 1; i <= n; ++i) {
if (dis[i] > maxx) {
maxx = dis[i];
sum = in[i];
} else if (dis[i] == maxx) {
sum = (sum + in[i]) % mod;
}
}
printf("%lld\n%lld\n", maxx, sum);
}
long long anss;
void calc() {
for (int i = 1; i <= n; ++i) {
if (dis[i] == dis[ans]) {
anss = (anss + in[i]) % mod;
}
}
}
int main() {
freopen("a.in", "r", stdin);
freopen("a.out", "w", stdout);
scanf("%d%d%lld", &n, &m, &mod);
for (int i = 1; i <= m; ++i) {
scanf("%d%d", &a[i], &b[i]);
add(a[i], b[i]);
}
for (int i = 1; i <= n; ++i) {
if (!dfn[i]) {
tarjan(i);
}
}
build();
toposort();
calc();
return 0;
}

5. 注意事项

在缩点完成后,需要对结果进行去重操作,否则计算出的方案数会被高估。这种方法可以有效避免重复计数,确保最终结果的准确性。

上一篇:【树的重心】POJ 3099 Go Go Gorelians
下一篇:P5022 旅行

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2025年03月23日 06时43分42秒