题目大意
题目是图片形式的,就简要说下题意算了
一个有向图 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 }
题目链接 & AC 通道