强连通分量的应用,详见《挑战程序设计》P324

模板(2019.7):

  1. namespace two_sat {
  2. int dfn[M*], low[M*], cnt, stk[M*], top, cmp[M*], tot, n;
  3. bool vis[M*];
  4. vector<int> g[M*];
  5. void init(int sz) {
  6. n = sz;
  7. }
  8. void add(int u, int v) {
  9. g[u].pb(v);
  10. }
  11. void tarjan(int u) {
  12. dfn[u] = low[u] = ++cnt;
  13. stk[++top] = u;
  14. vis[u] = true;
  15. for (int v : g[u]) {
  16. if(!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
  17. else if(vis[v]) low[u] = min(low[u], dfn[v]);
  18. }
  19. if(dfn[u] == low[u]) {
  20. cmp[u] = ++tot;
  21. while(stk[top] != u) cmp[stk[top]] = tot, vis[stk[top--]] = false;
  22. vis[stk[top--]] = false;
  23. }
  24. }
  25. bool ck() {
  26. for (int i = ; i <= *n; ++i) if(!dfn[i]) tarjan(i);
  27. for (int i = ; i <= n; ++i) {
  28. if(cmp[i] == cmp[i+n]) return false;
  29. }
  30. return true;
  31. }
  32. }

例题1:HDU Peaceful Commission

思路:强连通分量分解,看有没有两个同一个国家的代表在一个强连通分量里,如果有,就是NIE。这个不是关键,关键是怎么输出,输出还要用一下dfs,把所有能到达的点标记一下,顺便判断一下和之前有没有矛盾,有矛盾的话所有被标记的点又要重新标记回去。其实这道题可以不用强连通分量分解,直接dfs。

代码1(强连通分量分解+dfs):

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define pb push_back
  5. #define mem(a,b) memset(a,b,sizeof(a))
  6.  
  7. int n,m,u,v;
  8. const int N=2e4+;
  9. vector<int>g[N];
  10. vector<int>rg[N];
  11. vector<int>vs;
  12. bool vis[N];
  13. bool vis1[N];
  14. int cmp[N];
  15. void add_edge(int u,int v)
  16. {
  17. g[u].pb(v);
  18. rg[v].pb(u);
  19. }
  20. void dfs(int u)
  21. {
  22. vis[u]=true;
  23. for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
  24. vs.pb(u);
  25. }
  26. void rdfs(int u,int k)
  27. {
  28. vis[u]=true;
  29. cmp[u]=k;
  30. for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
  31. }
  32. int scc()
  33. {
  34. mem(vis,false);
  35. vs.clear();
  36. for(int i=;i<*n;i++)if(!vis[i])dfs(i);
  37.  
  38. mem(vis,false);
  39. int k=;
  40. for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
  41. return k;
  42. }
  43. void init()
  44. {
  45. for(int i=;i<=*n;i++)g[i].clear(),rg[i].clear();
  46. }
  47. bool DFS(int u)
  48. {
  49. vis[u]=true;
  50. vis1[u]=true;
  51. if(vis[u^])return false;
  52. for(int i=;i<g[u].size();i++)
  53. {
  54. if(!vis[g[u][i]]&&!DFS(g[u][i]))return false;
  55. }
  56. return true;
  57. }
  58. void red(int u)
  59. {
  60. vis1[u]=false;
  61. vis[u]=false;
  62. for(int i=;i<g[u].size();i++)
  63. {
  64. if(vis1[g[u][i]])red(g[u][i]);
  65. }
  66. }
  67. int main()
  68. {
  69. ios::sync_with_stdio(false);
  70. cin.tie();
  71. while(cin>>n>>m)
  72. {
  73. init();
  74. for(int i=;i<m;i++)
  75. {
  76. cin>>u>>v;
  77. u--;
  78. v--;
  79. add_edge(u,v^);
  80. add_edge(v,u^);
  81. }
  82. int t=scc();
  83. bool flag=false;
  84. for(int i=;i<*n;i+=)if(cmp[i]==cmp[i+]){cout<<"NIE"<<endl;flag=true;break;}
  85. if(flag)continue;
  86. mem(vis,false);
  87. mem(vis1,false);
  88. for(int i=;i<*n;i+=)
  89. {
  90. if(vis[i])
  91. {
  92. cout<<i+<<endl;
  93. continue;
  94. }
  95. else if(vis[i+])
  96. {
  97. cout<<i+<<endl;
  98. continue;
  99. }
  100. else
  101. {
  102. if(DFS(i))
  103. {
  104. cout<<i+<<endl;
  105. }
  106. else
  107. {
  108. red(i);
  109. cout<<i+<<endl;
  110. DFS(i+);
  111. }
  112. }
  113. }
  114. }
  115. return ;
  116. }

代码2(dfs):

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define pb push_back
  5. #define mem(a,b) memset(a,b,sizeof(a))
  6.  
  7. int n,m,u,v,tot;
  8. const int N=2e4+;
  9. vector<int>g[N];
  10. vector<int>rg[N];
  11. vector<int>vs;
  12. bool vis[N];
  13. int cmp[N];
  14. int s[N];
  15. void add_edge(int u,int v)
  16. {
  17. g[u].pb(v);
  18. rg[v].pb(u);
  19. }
  20. /*void dfs(int u)
  21. {
  22. vis[u]=true;
  23. for(int i=0;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
  24. vs.pb(u);
  25. }
  26. void rdfs(int u,int k)
  27. {
  28. vis[u]=true;
  29. cmp[u]=k;
  30. for(int i=0;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
  31. }
  32. int scc()
  33. {
  34. mem(vis,false);
  35. vs.clear();
  36. for(int i=0;i<2*n;i++)if(!vis[i])dfs(i);
  37.  
  38. mem(vis,false);
  39. int k=0;
  40. for(int i=vs.size()-1;i>=0;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
  41. return k;
  42. }*/
  43. void init()
  44. {
  45. for(int i=;i<=*n;i++)g[i].clear(),rg[i].clear();
  46. }
  47. bool DFS(int u)
  48. {
  49. if(vis[u^])return false;
  50. if(vis[u])return true;
  51. vis[u]=true;
  52. s[tot++]=u;
  53. for(int i=;i<g[u].size();i++)
  54. {
  55. if(!DFS(g[u][i]))return false;
  56. }
  57. return true;
  58. }
  59. bool solve()
  60. {
  61. mem(vis,false);
  62. for(int i=;i<*n;i+=)
  63. {
  64. if(vis[i]||vis[i^])continue;
  65. tot=;
  66. if(!DFS(i))
  67. {
  68. while(tot)vis[s[--tot]]=false;
  69. if(!DFS(i^))return false;
  70. }
  71. }
  72. return true;
  73. }
  74. int main()
  75. {
  76. ios::sync_with_stdio(false);
  77. cin.tie();
  78. while(cin>>n>>m)
  79. {
  80. init();
  81. for(int i=;i<m;i++)
  82. {
  83. cin>>u>>v;
  84. u--;
  85. v--;
  86. add_edge(u,v^);
  87. add_edge(v,u^);
  88. }
  89. //int t=scc();
  90. if(solve())
  91. {
  92. for(int i=;i<*n;i++)
  93. if(vis[i])cout<<i+<<endl;
  94. }
  95. else cout<<"NIE"<<endl;
  96. }
  97. return ;
  98. }

例题2:POJ 3207 Ikki's Story IV - Panda's Trick

思路:由于题目说每个点最多只能连一次,所以我直接用起点的坐标映射成它在圆内,+n后映射成它在圆外。不过这样求强连通时就要遍历每个点了,有点慢。

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. #include<vector>
  7. using namespace std;
  8. #define ll long long
  9. #define pb push_back
  10. #define mem(a,b) memset(a,b,sizeof(a))
  11.  
  12. const int N=2e3+;
  13. vector<int>g[N];
  14. vector<int>rg[N];
  15. vector<int>vs;
  16. vector<int>s;
  17. bool vis[N];
  18. int cmp[N];
  19. int n,m,u,v;
  20. int a[N];
  21. void add_edge(int u,int v)
  22. {
  23. g[u].pb(v);
  24. rg[v].pb(u);
  25. }
  26. void dfs(int u)
  27. {
  28. vis[u]=true;
  29. for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
  30. vs.pb(u);
  31. }
  32. void rdfs(int u,int k)
  33. {
  34. vis[u]=true;
  35. cmp[u]=k;
  36. for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
  37. }
  38. int scc()
  39. {
  40. mem(vis,false);
  41. vs.clear();
  42. for(int i=;i<*n;i++)if(!vis[i])dfs(i);
  43.  
  44. int k=;
  45. mem(vis,false);
  46. for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
  47. return k;
  48. }
  49. int main()
  50. {
  51. ios::sync_with_stdio(false);
  52. cin.tie();
  53. mem(a,-);
  54. cin>>n>>m;
  55. for(int i=;i<m;i++)
  56. {
  57. cin>>u>>v;
  58. a[u]=v;
  59. a[v]=u;
  60. s.pb(u);
  61. s.pb(v);
  62. }
  63.  
  64. sort(s.begin(),s.end());
  65. for(int i=;i<s.size();i++)
  66. {
  67. for(int j=i+;j<s.size();j++)
  68. {
  69. int l=min(s[i],a[s[i]]),r=max(s[i],a[s[i]]);
  70. int _l=min(s[j],a[s[j]]),_r=max(s[j],a[s[j]]);
  71. if((r>_l&&r<_r&&l<_l)||(l<_r&&l>_l&&r>_r))
  72. {
  73. add_edge(s[i],s[j]+n);
  74. add_edge(s[j],s[i]+n);
  75. add_edge(s[i]+n,s[j]);
  76. add_edge(s[j]+n,s[i]);
  77. }
  78. }
  79. }
  80.  
  81. int t=scc();
  82. for(int i=;i<s.size();i++)
  83. if(cmp[s[i]]==cmp[s[i]+n])
  84. {
  85. cout<<"the evil panda is lying again"<<endl;
  86. return ;
  87. }
  88. cout<<"panda is telling the truth..."<<endl;
  89. return ;
  90. }

例题3:POJ 3683 Priest John's Busiest Day

思路:大白书上的是输出拓扑序大的,不懂,留坑。

补坑:拓扑序大的没有边连向拓扑序小的强联通,所以不会产生矛盾。

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. #include<vector>
  7. using namespace std;
  8. #define ll long long
  9. #define pb push_back
  10. #define mem(a,b) memset(a,b,sizeof(a))
  11.  
  12. const int N=2e3+;
  13. vector<int>g[N];
  14. vector<int>rg[N];
  15. vector<int>vs;
  16. bool vis[N];
  17. int cmp[N];
  18. int n,m,u,v;
  19. int S[N],T[N],D[N];
  20. void add_edge(int u,int v)
  21. {
  22. g[u].pb(v);
  23. rg[v].pb(u);
  24. }
  25. void dfs(int u)
  26. {
  27. vis[u]=true;
  28. for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
  29. vs.pb(u);
  30. }
  31. void rdfs(int u,int k)
  32. {
  33. vis[u]=true;
  34. cmp[u]=k;
  35. for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
  36. }
  37. int scc()
  38. {
  39. mem(vis,false);
  40. vs.clear();
  41. for(int i=;i<*n;i++)if(!vis[i])dfs(i);
  42.  
  43. int k=;
  44. mem(vis,false);
  45. for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
  46. return k;
  47. }
  48. void init()
  49. {
  50. for(int i=;i<=n;i++)g[i].clear(),rg[i].clear();
  51. }
  52. void solve()
  53. {
  54. mem(vis,false);
  55. }
  56. int main()
  57. {
  58. int a,b,c,d;
  59. while(~scanf("%d",&n))
  60. {
  61. init();
  62. for(int i=;i<n;i++)
  63. {
  64. scanf("%d:%d %d:%d %d",&a,&b,&c,&d,&D[i]);
  65. S[i]=a*+b;
  66. T[i]=c*+d;
  67. }
  68. for(int i=;i<n;i++)
  69. {
  70. for(int j=i+;j<n;j++)
  71. {
  72. if(min(S[i]+D[i],S[j]+D[j])>max(S[i],S[j]))add_edge(i,n+j),add_edge(j,n+i);
  73. if(min(T[i],T[j])>max(T[i]-D[i],T[j]-D[j]))add_edge(n+j,i),add_edge(n+i,j);
  74. if(min(T[i],S[j]+D[j])>max(T[i]-D[i],S[j]))add_edge(n+i,n+j),add_edge(j,i);
  75. if(min(T[j],S[i]+D[i])>max(T[j]-D[j],S[i]))add_edge(i,j),add_edge(n+j,n+i);
  76. }
  77. }
  78. int t=scc();
  79. bool flag=false;
  80. for(int i=;i<n;i++)if(cmp[i]==cmp[i+n]){
  81. flag=true;
  82. break;
  83. }
  84. if(flag)printf("NO\n");
  85. else
  86. {
  87. printf("YES\n");
  88. for(int i=;i<n;i++)
  89. {
  90. if(cmp[i]>cmp[n+i])
  91. printf("%02d:%02d %02d:%02d\n",S[i]/,S[i]%,(S[i]+D[i])/,(S[i]+D[i])%);
  92. else printf("%02d:%02d %02d:%02d\n",(T[i]-D[i])/,(T[i]-D[i])%,T[i]/,T[i]%);
  93. }
  94. }
  95. }
  96. return ;
  97. }

例题4:POJ 3678 Katu Puzzle

思路:一开始看起来很复杂,其实只要建好边就好了。

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. #include<vector>
  7. using namespace std;
  8. #define ll long long
  9. #define pb push_back
  10. #define mem(a,b) memset(a,b,sizeof(a))
  11.  
  12. const int N=2e3+;
  13. vector<int>g[N];
  14. vector<int>rg[N];
  15. vector<int>vs;
  16. vector<int>s;
  17. bool vis[N];
  18. int cmp[N];
  19. int n,m,u,v;
  20. int a[N];
  21. void add_edge(int u,int v)
  22. {
  23. g[u].pb(v);
  24. rg[v].pb(u);
  25. }
  26. void dfs(int u)
  27. {
  28. vis[u]=true;
  29. for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
  30. vs.pb(u);
  31. }
  32. void rdfs(int u,int k)
  33. {
  34. vis[u]=true;
  35. cmp[u]=k;
  36. for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
  37. }
  38. int scc()
  39. {
  40. mem(vis,false);
  41. vs.clear();
  42. for(int i=;i<*n;i++)if(!vis[i])dfs(i);
  43.  
  44. int k=;
  45. mem(vis,false);
  46. for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
  47. return k;
  48. }
  49. void init()
  50. {
  51. for(int i=;i<=*n;i++)g[i].clear(),rg[i].clear();
  52. }
  53. int main()
  54. {
  55. ios::sync_with_stdio(false);
  56. cin.tie();
  57. int a;
  58. string t;
  59. while(cin>>n>>m)
  60. {
  61. for(int i=;i<m;i++)
  62. {
  63. cin>>u>>v>>a>>t;
  64. if(t[]=='A')
  65. {
  66. if(a==)
  67. {
  68. add_edge(u,v);
  69. add_edge(v,u);
  70. add_edge(u+n,u);
  71. add_edge(v+n,v);
  72. }
  73. else
  74. {
  75. add_edge(u,v+n);
  76. add_edge(v,u+n);
  77. }
  78. }
  79. else if(t[]=='O')
  80. {
  81. if(a==)
  82. {
  83. add_edge(u+n,v);
  84. add_edge(v+n,u);
  85. }
  86. else
  87. {
  88. add_edge(u+n,v+n);
  89. add_edge(v+n,u+n);
  90. add_edge(u,u+n);
  91. add_edge(v,v+n);
  92. }
  93. }
  94. else if(t[]=='X')
  95. {
  96. if(a==)
  97. {
  98. add_edge(u,v+n);
  99. add_edge(v,u+n);
  100. add_edge(v+n,u);
  101. add_edge(u+n,v);
  102. }
  103. else
  104. {
  105. add_edge(u,v);
  106. add_edge(v,u);
  107. add_edge(v+n,u+n);
  108. add_edge(u+n,v+n);
  109. }
  110. }
  111. }
  112. scc();
  113. bool flag=false;
  114. for(int i=;i<n;i++)
  115. if(cmp[i]==cmp[i+n]){
  116. flag=true;
  117. break;
  118. }
  119. if(flag)cout<<"NO"<<endl;
  120. else cout<<"YES"<<endl;
  121. }
  122. return ;
  123. }

例题5:POJ 3648 Wedding

思路:与例1相同,不过要建一条0号新娘和他新郎的边,这样只会选出新郎那一边的人。

代码:

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<algorithm>
  6. #include<vector>
  7. using namespace std;
  8. #define ll long long
  9. #define pb push_back
  10. #define mem(a,b) memset(a,b,sizeof(a))
  11.  
  12. const int N=;
  13. vector<int>g[N];
  14. vector<int>rg[N];
  15. vector<int>vs;
  16. bool vis[N];
  17. int cmp[N];
  18. int s[N];
  19. int n,m,u,v;
  20. int tot=;
  21. void add_edge(int u,int v)
  22. {
  23. g[u].pb(v);
  24. rg[v].pb(u);
  25. }
  26. void dfs(int u)
  27. {
  28. vis[u]=true;
  29. for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
  30. vs.pb(u);
  31. }
  32. void rdfs(int u,int k)
  33. {
  34. vis[u]=true;
  35. cmp[u]=k;
  36. for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
  37. }
  38. int scc()
  39. {
  40. mem(vis,false);
  41. vs.clear();
  42. for(int i=;i<*n;i++)if(!vis[i])dfs(i);
  43.  
  44. mem(vis,false);
  45. int k=;
  46. for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
  47. return k;
  48. }
  49. void init()
  50. {
  51. for(int i=;i<=*n;i++)g[i].clear(),rg[i].clear();
  52. }
  53. bool DFS(int u)
  54. {
  55. if(u<n&&vis[u+n])return false;
  56. if(u>=n&&vis[u-n])return false;
  57. if(vis[u])return true;
  58. vis[u]=true;
  59. s[tot++]=u;
  60. for(int i=;i<g[u].size();i++)
  61. {
  62. if(!DFS(g[u][i]))return false;
  63. }
  64. return true;
  65. }
  66. bool solve()
  67. {
  68. mem(vis,false);
  69. for(int i=;i<*n;i++)
  70. {
  71. if(vis[i])continue;
  72. if(i>=n&&vis[i-n])continue;
  73. if(i<n&&vis[i+n])continue;
  74. tot=;
  75. if(!DFS(i))
  76. {
  77. while(tot)vis[s[--tot]]=false;
  78. if(i<n&&!DFS(i+n))return false;
  79. if(i>=n&&!DFS(i-n))return false;
  80. }
  81. }
  82. return true;
  83. }
  84. int main()
  85. {
  86. ios::sync_with_stdio(false);
  87. cin.tie();
  88. string s1,s2;
  89. while(cin>>n>>m)
  90. {
  91. if(n==&&m==)break;
  92. init();
  93. for(int i=;i<m;i++)
  94. {
  95. cin>>s1>>s2;
  96. int t=;
  97. for(int j=;j<s1.size();j++)
  98. {
  99. if(''<=s1[j]&&s1[j]<='')t=t*+s1[j]-'';
  100. else
  101. {
  102. if(s1[j]=='w')
  103. {
  104. u=t;
  105. }
  106. else
  107. {
  108. u=n+t;
  109. }
  110. }
  111. }
  112. t=;
  113. for(int j=;j<s2.size();j++)
  114. {
  115. if(''<=s2[j]&&s2[j]<='')t=t*+s2[j]-'';
  116. else
  117. {
  118. if(s2[j]=='w')
  119. {
  120. v=t;
  121. }
  122. else
  123. {
  124. v=n+t;
  125. }
  126. }
  127. }
  128. if(v<n)add_edge(u,v+n);
  129. else add_edge(u,v-n);
  130. if(u<n)add_edge(v,u+n);
  131. else add_edge(v,u-n);
  132. }
  133. add_edge(,n);
  134. int t=scc();
  135. bool flag=false;
  136. for(int i=;i<n;i++)
  137. {
  138. if(cmp[i]==cmp[i+n])
  139. {
  140. flag=true;
  141. break;
  142. }
  143. }
  144. if(flag)
  145. {
  146. cout<<"bad luck"<<endl;
  147. }
  148. else
  149. {
  150. tot=;
  151. mem(vis,false);
  152. solve();
  153. for(int i=;i<n;i++)
  154. {
  155. if(vis[i])cout<<i<<"h";
  156. else cout<<i<<"w";
  157. if(i!=n-)cout<<' ';
  158. }
  159. cout<<endl;
  160. }
  161. }
  162. return ;
  163. }

例题6:Codeforces 468B - Two Sets

思路:如果x在a集合中,那么a-x不存在的话,x一定在b集合(x在a==>x在b);如果a-x存在,那么有(原命题:x在a==>a-x在a,逆否命题:a-x在b==>x在b),同理,x在b集合中也是一样的。建边建好了就可以输出拓扑序大的了。

代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define pb push_back
  5. #define mem(a,b) memset(a,b,sizeof(a))
  6. const int N=2e5+;
  7. vector<int>g[N];
  8. vector<int>rg[N];
  9. vector<int>vs;
  10. int belong[N]={};
  11. bool vis[N];
  12. int cmp[N];
  13. int s[N];
  14. int n;
  15. int tot=;
  16. struct node
  17. {
  18. int v,id;
  19. bool operator < (node t)const
  20. {
  21. return v<t.v;
  22. }
  23. }a[N];
  24. void add_edge(int u,int v)
  25. {
  26. g[u].pb(v);
  27. rg[v].pb(u);
  28. }
  29. void dfs(int u)
  30. {
  31. vis[u]=true;
  32. for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
  33. vs.pb(u);
  34. }
  35. void rdfs(int u,int k)
  36. {
  37. vis[u]=true;
  38. cmp[u]=k;
  39. for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
  40. }
  41. int scc()
  42. {
  43. mem(vis,false);
  44. vs.clear();
  45. for(int i=;i<*n;i++)if(!vis[i])dfs(i);
  46.  
  47. mem(vis,false);
  48. int k=;
  49. for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
  50. return k;
  51. }
  52. /*bool DFS(int u)
  53. {
  54. if(u<n&&vis[u+n])return false;
  55. if(u>=n&&vis[u-n])return false;
  56. if(vis[u])return true;
  57. vis[u]=true;
  58. s[tot++]=u;
  59. for(int i=0;i<g[u].size();i++)
  60. {
  61. if(!DFS(g[u][i]))return false;
  62. }
  63. return true;
  64. }
  65. bool solve()
  66. {
  67. mem(vis,false);
  68. for(int i=0;i<2*n;i++)
  69. {
  70. if(vis[i])continue;
  71. if(i>=n&&vis[i-n])continue;
  72. if(i<n&&vis[i+n])continue;
  73. tot=0;
  74. if(!DFS(i))
  75. {
  76. while(tot)vis[s[--tot]]=false;
  77. if(i<n&&!DFS(i+n))return false;
  78. if(i>=n&&!DFS(i-n))return false;
  79. }
  80. }
  81. return true;
  82. } */
  83. int main()
  84. {
  85. ios::sync_with_stdio(false);
  86. cin.tie();
  87. int A,B;
  88. cin>>n>>A>>B;
  89. for(int i=;i<n;i++)cin>>a[i].v,a[i].id=i;
  90. sort(a,a+n);
  91.  
  92. for(int i=;i<n;i++)
  93. {
  94. //bool flag=false;
  95. int t=lower_bound(a,a+n,node{A-a[i].v,})-a;
  96. if(t!=n&&a[t].v==A-a[i].v)
  97. {
  98. //flag=true;
  99. add_edge(a[i].id,a[t].id);
  100. add_edge(a[t].id+n,a[i].id+n);
  101. }
  102. else
  103. {
  104. add_edge(a[i].id,a[i].id+n);
  105. }
  106. t=lower_bound(a,a+n,node{B-a[i].v,})-a;
  107. if(t!=n&&a[t].v==B-a[i].v)
  108. {
  109. //flag=true;
  110. add_edge(a[i].id+n,a[t].id+n);
  111. add_edge(a[t].id,a[i].id);
  112. }
  113. else
  114. {
  115. add_edge(a[i].id+n,a[i].id);
  116. }
  117. /*if(!flag)
  118. {
  119. cout<<"NO"<<endl;
  120. return 0;
  121. }*/
  122. }
  123. int t=scc();
  124. for(int i=;i<n;i++)
  125. if(cmp[i]==cmp[i+n])
  126. {
  127. cout<<"NO"<<endl;
  128. return ;
  129. }
  130. cout<<"YES"<<endl;
  131. //solve();
  132. for(int i=;i<n;i++)if(cmp[i]>cmp[i+n])cout<<<<' ';else cout<<<<' ';
  133. cout<<endl;
  134. return ;
  135. }

代码复杂了,因为所有数不同,所以数可以直接映射成下标。

例题7:Codeforces 867E National Property

思路:学会建矛盾边(自己的叫法)。

代码:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. #define ll long long
  4. #define pb push_back
  5. #define mem(a,b) memset(a,b,sizeof(a))
  6. const int N=2e5+;
  7. vector<int>g[N];
  8. vector<int>rg[N];
  9. vector<int>vs;
  10. vector<int>a[N];
  11. bool vis[N];
  12. int cmp[N];
  13. int n,m;
  14. void add_edge(int u,int v)
  15. {
  16. g[u].pb(v);
  17. rg[v].pb(u);
  18. }
  19. void dfs(int u)
  20. {
  21. vis[u]=true;
  22. for(int i=;i<g[u].size();i++)if(!vis[g[u][i]])dfs(g[u][i]);
  23. vs.pb(u);
  24. }
  25. void rdfs(int u,int k)
  26. {
  27. vis[u]=true;
  28. cmp[u]=k;
  29. for(int i=;i<rg[u].size();i++)if(!vis[rg[u][i]])rdfs(rg[u][i],k);
  30. }
  31. int scc()
  32. {
  33. mem(vis,false);
  34. vs.clear();
  35. for(int i=;i<=*m;i++)if(!vis[i])dfs(i);
  36.  
  37. mem(vis,false);
  38. int k=;
  39. for(int i=vs.size()-;i>=;i--)if(!vis[vs[i]])rdfs(vs[i],k++);
  40. return k;
  41. }
  42. void init()
  43. {
  44. for(int i=;i<=*m;i++)g[i].clear(),rg[i].clear();
  45. for(int i=;i<n;i++)a[i].clear();
  46. }
  47. int main()
  48. {
  49. ios::sync_with_stdio(false);
  50. cin.tie();
  51. int t,b;
  52. while(cin>>n>>m)
  53. {
  54. init();
  55. for(int i=;i<n;i++)
  56. {
  57. cin>>t;
  58. for(int j=;j<t;j++)cin>>b,a[i].pb(b);
  59. }
  60. bool f=false;
  61. for(int i=;i<n-;i++)
  62. {
  63. //cout<<a[i].size()<<endl;
  64. if(a[i].size()<=a[i+].size())
  65. {
  66. for(int j=;j<a[i].size();j++)
  67. {
  68. if(a[i][j]<a[i+][j])
  69. {
  70. add_edge(a[i][j],a[i+][j]);
  71. add_edge(a[i+][j]+m,a[i][j]+m);
  72. break;
  73. }
  74. else if(a[i][j]>a[i+][j])
  75. {
  76. add_edge(a[i][j],a[i][j]+m);
  77. add_edge(a[i+][j]+m,a[i+][j]);
  78. add_edge(a[i][j]+m,a[i+][j]);
  79. add_edge(a[i+][j],a[i][j]+m);
  80. break;
  81. }
  82. }
  83. }
  84. else
  85. {
  86. bool flag=false;
  87. for(int j=;j<a[i+].size();j++)
  88. {
  89. if(a[i][j]<a[i+][j])
  90. {
  91. flag=true;
  92. add_edge(a[i][j],a[i+][j]);
  93. add_edge(a[i+][j]+m,a[i][j]+m);
  94. break;
  95. }
  96. else if(a[i][j]>a[i+][j])
  97. {
  98. flag=true;
  99. add_edge(a[i][j],a[i][j]+m);
  100. add_edge(a[i+][j]+m,a[i+][j]);
  101. add_edge(a[i][j]+m,a[i+][j]);
  102. add_edge(a[i+][j],a[i][j]+m);
  103. break;
  104. }
  105. }
  106. if(!flag)
  107. {
  108. f=true;
  109. break;
  110. }
  111. }
  112. }
  113. if(f)cout<<"No"<<endl;
  114. else
  115. {
  116. //cout<<1<<endl;
  117. scc();
  118. for(int i=;i<=m;i++)
  119. {
  120. if(cmp[i]==cmp[i+m])
  121. {
  122. f=true;
  123. // cout<<i<<endl;
  124. break;
  125. }
  126. }
  127. if(f)cout<<"No"<<endl;
  128. else
  129. {
  130. cout<<"Yes"<<endl;
  131. int cnt=;
  132. for(int i=;i<=m;i++)
  133. if(cmp[i]<cmp[i+m])cnt++;
  134. cout<<cnt<<endl;
  135. for(int i=;i<=m;i++)if(cmp[i]<cmp[i+m])cout<<i<<' ';
  136. cout<<endl;
  137. }
  138. }
  139. }
  140. return ;
  141. }

总结:

关于建边:如果x一定不能选,那么建一条x连向!x的边。

关于输出:2-sat问题的输出如果没有限制条件,那么直接输出拓扑序大的一组答案,如果有限制条件,DFS染色标记后再输出答案。

算法笔记--2-sat的更多相关文章

  1. 学习Java 以及对几大基本排序算法(对算法笔记书的研究)的一些学习总结(Java对算法的实现持续更新中)

    Java排序一,冒泡排序! 刚刚开始学习Java,但是比较有兴趣研究算法.最近看了一本算法笔记,刚开始只是打算随便看看,但是发现这本书非常不错,尤其是对排序算法,以及哈希函数的一些解释,让我非常的感兴 ...

  2. 算法笔记--数位dp

    算法笔记 这个博客写的不错:http://blog.csdn.net/wust_zzwh/article/details/52100392 数位dp的精髓是不同情况下sta变量的设置. 模板: ]; ...

  3. 算法笔记--lca倍增算法

    算法笔记 模板: vector<int>g[N]; vector<int>edge[N]; ][N]; int deep[N]; int h[N]; void dfs(int ...

  4. 算法笔记--STL中的各种遍历及查找(待增)

    算法笔记 map: map<string,int> m; map<string,int>::iterator it;//auto it it = m.begin(); whil ...

  5. 算法笔记--priority_queue

    算法笔记 priority_queue<int>que;//默认大顶堆 或者写作:priority_queue<int,vector<int>,less<int&g ...

  6. 算法笔记--sg函数详解及其模板

    算法笔记 参考资料:https://wenku.baidu.com/view/25540742a8956bec0975e3a8.html sg函数大神详解:http://blog.csdn.net/l ...

  7. 算法笔记——C/C++语言基础篇(已完结)

    开始系统学习算法,希望自己能够坚持下去,期间会把常用到的算法写进此博客,便于以后复习,同时希望能够给初学者提供一定的帮助,手敲难免存在错误,欢迎评论指正,共同学习.博客也可能会引用别人写的代码,如有引 ...

  8. 算法笔记_067:蓝桥杯练习 算法训练 安慰奶牛(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路.道路被用来连接N个牧场,牧场被连续地编号为1到N.每一个牧场都是 ...

  9. 算法笔记(c++)--回文

    算法笔记(c++)--回文 #include<iostream> #include<algorithm> #include<vector> using namesp ...

  10. 算法笔记(c++)--完全背包问题

    算法笔记(c++)--完全背包和多重背包问题 完全背包 完全背包不同于01背包-完全背包里面的东西数量无限 假设现在有5种物品重量为5,4,3,2,1  价值为1,2,3,4,5  背包容量为10 # ...

随机推荐

  1. 008-centos服务管理

  2. 3:2 OGNL 简介

    OGNL : (对象图导航语言) 从一个对象到另一个对象 OGNL来源于Xwork: OGNL的作用: OGNL在数据进出值栈的时候进行类型转换

  3. 《算法C语言实现》————三道题目

    1.对于N = 10,100和1000,记录你的运行环境中分别运行一下程序所花费的时间.(用python) import datetime global a a = 0 def time_1(s): ...

  4. C++矩阵库 Eigen 快速入门

    最近需要用 C++ 做一些数值计算,之前一直采用Matlab 混合编程的方式处理矩阵运算,非常麻烦,直到发现了 Eigen 库,简直相见恨晚,好用哭了. Eigen 是一个基于C++模板的线性代数库, ...

  5. Python: 字符串开头或结尾匹配str.startswith(),str.endswith()

    问题 需要通过指定的文本模式去检查字符串的开头或者结尾,比如文件名后缀,URLScheme 等等. 解决方案 1.检查字符串开头或结尾的一个简单方法是使用str.startswith() 或者是str ...

  6. react脚手架构建工程

    https://blog.csdn.net/qtfying/article/details/78665664 第二步:安装less包: https://segmentfault.com/a/11900 ...

  7. 神州行省内流量套餐6元500M申请,发送BLSN6到10086即可

    神州行流量套餐,神州行省内流量套餐6元500M申请,发送BLSN6到10086即可申请开通专属流量包,比全国5元30M划算多了4G全国流量套餐 5元/30M 10元/100M 20元/300M 30元 ...

  8. Qt 学习之路 2(55):数据库操作

    Qt 提供了 QtSql 模块来提供平台独立的基于 SQL 的数据库操作.这里我们所说的“平台独立”,既包括操作系统平台,又包括各个数据库平台.另外,我们强调了“基于 SQL”,因为 NoSQL 数据 ...

  9. hibernate的实现原理以及延迟加载

    Hibernate是怎样实现呢?主要是依据反射机制. 现在以一次数据库查询操作分析Hibernate实现原理. 假设有一个用户表(tbl_user),表中字段有id,name,sex.同时有一个实体类 ...

  10. PowerDesigner 教程

    摘自:http://www.cnblogs.com/advocate/p/3730027.html 目标:本文主要介绍PowerDesigner中概念数据模型 CDM的基本概念. 一.概念数据模型概述 ...