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 一遍,统计每个点出发能走出多少条最长链,最后统计求和即可

 

参考代码

 

BZOJ 1093 [ZJOI2007] 最大半连通子图(强联通缩点+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\n%d\n", ans1, ans2);

97     return 0;

98 }
BZOJ 1093

 

题目链接 & AC 通道

 

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

 

 

 

你可能感兴趣的:(2007)