最大半连通子图 bzoj 1093

最大半连通子图 (1.5s 128MB) semi

【问题描述】

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

若满足,则称G’是G的一个导出子图。

若G’是G的导出子图,且G’半连通,则称G’为G的半连通子图。

若G’是G所有半连通子图中包含节点数最多的,则称G’是G的最大半连通子图。

给定一个有向图G,请求出G的最大半连通子图拥有的节点数K,以及不同的最大半连通子图的数目C。由于C可能比较大,仅要求输出C对X的余数。

【输入文件】

第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述。接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。

【输出文件】

应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.

【样例输入】

6 6 20070603

1 2

2 1

1 3

2 4

5 6

6 4

【样例输出】

3

3

【数据规模】

对于20%的数据, N ≤ 18;

对于60%的数据, N ≤ 10000;

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

对于100%的数据, X ≤ 108



题解:

主要算法:强连通分量;拓扑排序;递推;

首先用Tarjon缩点,去重连边,得到新图,那么题目就变成了求图中最长链及最长链个数

最长链可以直接用拓扑排序

最长链个数用一个类似递推的方法

记录每一个点的方案数

那么当前点的方案数就等于连到此点且满足距离相等的点的方案数之和

最后查找距离等于最长链的点,答案为它们的方案数之和

  1 #include<algorithm>
  2 #include<iostream>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<cstdio>
  6 #include<cmath>
  7 using namespace std;
  8 inline int Get()
  9 {
 10     int x = 0;
 11     char c = getchar();
 12     while(‘0‘ > c || c > ‘9‘) c = getchar();
 13     while(‘0‘ <= c && c <= ‘9‘)
 14     {
 15         x = (x << 3) + (x << 1) + c - ‘0‘;
 16         c = getchar();
 17     }
 18     return x;
 19 }
 20 const int me = 1000233;
 21 int n, m, mo;
 22 int x[me], y[me];
 23 int tot;
 24 int de[me], to[me], fir[me], nex[me];
 25 int ue[me];
 26 int si[me];
 27 inline void Ins(int x, int y)
 28 {
 29     nex[++tot] = fir[x];
 30     fir[x] = tot;
 31     to[tot] = y;
 32 }
 33 int num, top, col;
 34 int ti[me], lo[me], st[me], co[me];
 35 inline void Tarjan(int u)
 36 {
 37     ti[u] = lo[u] = ++num;
 38     st[++top] = u;
 39     for(int i = fir[u]; i; i = nex[i])
 40     {
 41         int v = to[i];
 42         if(!ti[v])
 43         {
 44             Tarjan(v);
 45             lo[u] = min(lo[u], lo[v]);
 46         }
 47         else
 48             if(!co[v])
 49                 lo[u] = min(lo[u], ti[v]);
 50     }
 51     if(lo[u] == ti[u])
 52     {
 53         co[u] = ++col;
 54         ++si[col];
 55         while(st[top] != u)
 56         {
 57             ++si[col];
 58             co[st[top]] = col;
 59             --top;
 60         }
 61         --top;
 62     }
 63 }
 64 int t, w;
 65 int ans;
 66 int e[me];
 67 int dis[me];
 68 inline bool rule(int a, int b)
 69 {
 70     if(x[a] != x[b]) return x[a] < x[b];
 71     return y[a] < y[b];
 72 }
 73 int nu[me];
 74 inline void Remove()
 75 {
 76     for(int i = 1; i <= m; ++i)
 77     {
 78         nu[i] = i;
 79         x[i] = co[x[i]];
 80         y[i] = co[y[i]];
 81     }
 82     sort(nu + 1, nu + 1 + m, rule);
 83 }
 84 inline void Build()
 85 {
 86     tot = 0;
 87     memset(fir, 0, sizeof(fir));
 88     for(int i = 1; i <= m; ++i)
 89     {
 90         int z = nu[i];
 91         if((x[z] != y[z]) && (x[z] != x[nu[i - 1]] || y[z] != y[nu[i - 1]]))
 92         {
 93             ++de[y[z]];
 94             Ins(x[z], y[z]);
 95         }
 96     }
 97 }
 98 inline void Reset()
 99 {
100     for(int i = 1; i <= col; ++i)
101         if(!de[i])
102         {
103             ue[++w] = i;
104             dis[i] = si[i];
105             e[i] = 1;
106             if(dis[ans] < dis[i]) ans = i;
107         }
108 }
109 inline void Topo()
110 {
111     while(t < w)
112     {
113         int u = ue[++t];
114         for(int i = fir[u]; i; i = nex[i])
115         {
116             int v = to[i];
117             --de[v];
118             if(dis[v] < dis[u] + si[v])
119             {
120                 dis[v] = dis[u] + si[v];
121                 e[v] = 0;
122                 if(dis[ans] < dis[v]) ans = v;
123             }
124             if(dis[v] == dis[u] + si[v])
125                 e[v] = (e[v] + e[u]) % mo;
126             if(!de[v]) ue[++w] = v;
127         }
128     }
129 }
130 int anss;
131 inline void Ask()
132 {
133     for(int i = 1; i <= n; ++i)
134         if(dis[i] == dis[ans])
135             anss = (anss + e[i]) % mo;
136 }
137 int main()
138 {
139     n = Get(), m = Get(), mo = Get();
140     for(int i = 1; i <= m; ++i)
141     {
142         x[i] = Get(), y[i] = Get();
143         Ins(x[i], y[i]);
144     }
145     for(int i = 1; i <= n; ++i)
146         if(!ti[i])
147             Tarjan(i);
148     Remove();
149     Build();
150     Reset();
151     Topo();
152     Ask();
153     printf("%d\n%d", dis[ans], anss);
154 }
时间: 2024-08-27 18:28:50

最大半连通子图 bzoj 1093的相关文章

BZOJ 1093 最大半连通子图(强连通分量+树形DP)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1093 题意: 思路:(1)首先,强连通分量中的一个点若在最大半连通子图中,则必定整个连通分量中的点都在,因为都在还是满足半连通的性质而且使得节点数更多. (2)因此,求出强连通分量缩点,形成一个有向无环图,其实与树是差不多的.在这个图上DP一次即可,也就是找出最长链以及最长链的个数. vector<int> g[N],g1[N]; int n,m,mod; int dfn[N],lo

BZOJ 1093: [ZJOI2007]最大半连通子图( tarjan + dp )

WA了好多次... 先tarjan缩点, 然后题意就是求DAG上的一条最长链. dp(u) = max{dp(v)} + totu, edge(u,v)存在. totu是scc(u)的结点数. 其实就是记忆化搜一下...重边就用set判一下 ------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring

bzoj 1093 [ZJOI2007]最大半连通子图(scc+DP)

1093: [ZJOI2007]最大半连通子图 Time Limit: 30 Sec  Memory Limit: 162 MBSubmit: 2286  Solved: 897[Submit][Status][Discuss] Description Input 第一行包含两个整数N,M,X.N,M分别表示图G的点数与边数,X的意义如上文所述.接下来M行,每行两个正整数a, b,表示一条有向边(a, b).图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次. Outpu

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

1093: [ZJOI2007]最大半连通子图 Time Limit: 30 Sec  Memory Limit: 162 MB Submit: 1732  Solved: 679 [Submit][Status] Description Input 第一行包含两个整数N,M,X.N,M分别表示图G的点数与边数,X的意义如上文所述.接下来M行,每行两个正整数a, b,表示一条有向边(a, b).图中的每个点将编号为1,2,3-N,保证输入中同一个(a,b)不会出现两次. Output 应包含两行

BZOJ 1093 ZJOI2007 最大半连通子图 Tarjan+动态规划

题目大意:定义半连通子图为一个诱导子图,其中任意两点(x,y)中x可到达y或y可到达x,求最大半连通子图的大小以及方案数 不就是个缩点之后拓扑序DP求最长链么 这题意逗不逗233333 注意缩点后连边不要连重复了 判重边那里我用了set... #include <set> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M

BZOJ 1093 ZJOI 2007 最大半连通子图 强联通分量+拓扑图DP

题目大意:定义半连通图:图中任意两点之间可以单向到达.求一个图的最大半连通子图,和这个图最大半连通子图的个数. 思路:半连通图并不是一定要没有环..这题意让我理解的.. 其实想法什么的不难,想明白了也不难写.因为要保证半连通,所以要先处理出一个图的联通状况.先用Tarjan缩点得到DAG,在这个DAG上找到最长链的长度就是第一问的答案.第二问可以先找到所有f值等于答案的点,在这些点上反向记忆化搜索DP. 注意一个小地方,ans的初值是最大的scc的大小,如果是0的化会wa一个点. CODE: #

tyvj——P3524 最大半连通子图

P3524 最大半连通子图 时间: 3000ms / 空间: 165536KiB / Java类名: Main 描述 输入格式 第一行包含两个整数N,M,X.N,M分别表示图G的点数与边数,X的意义如上文所述.接下来M行,每行两个正整数a, b,表示一条有向边(a, b).图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次. 输出格式 应包含两行,第一行包含一个整数K.第二行包含整数C Mod X. 测试样例1 输入 6 6 20070603 1 2 2 1 1 3 2

【tarjan 拓扑排序 dp】bzoj1093: [ZJOI2007]最大半连通子图

思维难度不大,关键考代码实现能力.一些细节还是很妙的. Description 一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径.若G'=(V',E')满足V'?V,E'是E中所有跟V'有关的边,则称G'是G的一个导出子图.若G'是G的导出子图,且G'半连通,则称G'为G的半连通子图.若G'是G所有半连通子图中包含节点数最多的,则称G'是G的最大半连通子图.给

[ZJOI2007]最大半连通子图

[ZJOI2007]最大半连通子图 题目大意: 一个有向图称为半连通的,当且仅当对于任意两点\(u,v\),都满足\(u\)能到达\(v\)或者\(v\)能到达\(u\). 给定一个\(n(n\le10^5)\)个点,\(m(m\le10^6)\)条边的有向图, 问该图最大半连通子图的节点个数及方案数. 思路: 缩点后在DAG上DP求带点权最长链,并统计方案数即可. 源代码: #include<stack> #include<queue> #include<cstdio>