BZOJ 1093 [ZJOI2007] 最大半连通子图(强联通缩点+DP)

题目大意

题目是图片形式的,就简要说下题意算了

一个有向图 G=(V, E) 称为半连通的(Semi-Connected),如果满足图中任意两点 u v,存在一条从 u 到 v 的路径或者从 v 到 u 的路径

给一个有向图(n 个点,m 条边),求出她的最大半连通子图中所包含的点数,以及这样的最大半连通子图有多少个(要求模上一个给定的数 x)

对于20%的数据, N 18;

对于60%的数据, N 10000;

对于100%的数据, N 100000, M 1000000;

对于100%的数据, X 10^8。

做法分析

这种题目在 POJ 做过类似的:POJ 2762 Going from u to v or from v to u? ,它只是询问一个图是否是半连通的,解题报告

这题其实也差不多的做法,先缩点,重新建图,使其成为一个 DAG,DAG 中每个点有一个点权表示这个点是原图中的几个点缩成的

新图中的一个最大半连通子图,必然是新图中的一个最长链(点权和最大),知道了这点之后,DP 就行了,类似于树形 DP,先求出从每个点出发,能走的最长链是多长,统计最长的那条就是最大半连通子图的点的数量了,至于怎么求有多少个最大半连通子图,也是一样的 DP 就行,在上一步的 DP 之后,再 DP 一遍,统计每个点出发能走出多少条最长链,最后统计求和即可

参考代码

 1 #include <iostream>
 2 #include <cstring>
 3 #include <cstdio>
 4 #include <stack>
 5 #include <vector>
 6 #include <set>
 7 
 8 using namespace std;
 9 
10 const int N=100005;
11 
12 set <pair<int, int> > tub;
13 stack <int> S;
14 vector <int> arc[N], adj[N];
15 int n, m, x, ans1, ans2, T, ind;
16 int id[N], low[N], dfn[N], cnt[N];
17 bool vs[N];
18 
19 void tarjan(int u) {
20     dfn[u]=low[u]=T++;
21     S.push(u), vs[u]=1;
22     for(int i=0, len=(int)adj[u].size(); i<len; i++) {
23         int v=adj[u][i];
24         if(dfn[v]==-1) {
25             tarjan(v);
26             if(low[u]>low[v]) low[u]=low[v];
27         }
28         else if(vs[v] && low[u]>dfn[v]) low[u]=dfn[v];
29     }
30     if(low[u]==dfn[u]) {
31         while(1) {
32             int v=S.top();
33             S.pop(), vs[v]=0;
34             id[v]=ind, cnt[ind]++;
35             if(v==u) break;
36         }
37         ind++;
38     }
39 }
40 
41 void DFS1(int u) {
42     vs[u]=1;
43     for(int i=0, len=(int)arc[u].size(); i<len; i++) {
44         int v=arc[u][i];
45         if(!vs[v]) DFS1(v);
46         dfn[u]=max(dfn[u], dfn[v]);
47     }
48     dfn[u]+=cnt[u];
49 }
50 
51 void DFS2(int u) {
52     vs[u]=1;
53     for(int i=0, len=(int)arc[u].size(); i<len; i++) {
54         int v=arc[u][i];
55         if(!vs[v]) DFS2(v);
56         if(dfn[u]==cnt[u]+dfn[v]) id[u]=(id[u]+id[v])%x;
57     }
58     if((int)arc[u].size()==0) id[u]=1;
59     if(dfn[u]==ans1) ans2=(ans2+id[u])%x;
60 }
61 
62 int main() {
63 //    freopen("in", "r", stdin);
64     scanf("%d%d%d", &n, &m, &x);
65     for(int i=1; i<=n; i++) adj[i].clear();
66     for(int i=0, a, b; i<m; i++) {
67         scanf("%d%d", &a, &b);
68         adj[a].push_back(b);
69     }
70     fill(dfn, dfn+1+n, -1);
71     fill(vs, vs+1+n, 0);
72     T=ind=0;
73     while(!S.empty()) S.pop();
74     for(int i=1; i<=n; i++) if(dfn[i]==-1) tarjan(i);
75     for(int i=0; i<ind; i++) arc[i].clear();
76     fill(low, low+ind, 0);
77     tub.clear();
78     for(int i=1; i<=n; i++)
79         for(int j=0, len=(int)adj[i].size(); j<len; j++) {
80             int v=id[adj[i][j]], u=id[i];
81             if(u==v) continue;
82             if(tub.find(make_pair(u, v))!=tub.end()) continue;
83             low[v]++, arc[u].push_back(v);
84             tub.insert(make_pair(u, v));
85         }
86     fill(vs, vs+ind, 0);
87     fill(dfn, dfn+ind, 0);
88     ans1=0, ans2=0;
89     for(int i=0; i<ind; i++) if(low[i]==0) {
90         DFS1(i);
91         ans1=max(ans1, dfn[i]);
92     }
93     fill(vs, vs+ind, 0);
94     fill(id, id+ind, 0);
95     for(int i=0; i<ind; i++) if(low[i]==0) DFS2(i);
96     printf("%d
%d
", ans1, ans2);
97     return 0;
98 }
BZOJ 1093

题目链接 & AC 通道

BZOJ 1093 [ZJOI2007] 最大半连通子图

原文地址:https://www.cnblogs.com/zhj5chengfeng/p/3267311.html