一些概念
点双模板(包含 割顶,桥,无向图)
#include <cstring>
#include <algorithm>
#include <stack>
#include <vector>
#define mst(x,a) memset(x,a,sizeof(x))
#define fzhead EDGE(int _u, int _v, int _next)
#define fzbody u(_u), v(_v), next(_next)
#define pb push_back
#define For(i,x,y) for(int i=(x); i<=(y); i++)
#define fori(i,x,y) for(int i=(x); i<(y); i++)
using namespace std;
const int maxm=2e6+10;
const int maxn=1e3+10;
struct EDGE{
int u,v,next;
EDGE(){}
fzhead:fzbody{}
}e[maxm];
int dfn[maxn],low[maxn],cut[maxn],bccno[maxn],dfs_clock,bcc_cnt;
/// bccno[maxn]存一个点是哪一个 点双连通分量。
int head[maxn],tot;
vector<int> bcc[maxn], bridge;
stack<EDGE> s;
void add(int bg, int to){
e[tot]=EDGE(bg,to,head[bg]);
head[bg]=tot++;
}
int root;
void tarjan(int u, int f){
dfn[u]=low[u]=++dfs_clock;
int child=0;
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].v;
EDGE edge = {u,v,0};
if(!dfn[v]){
s.push(edge);
child++;
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v]>dfn[u])bridge.push_back(i);
if(low[v] >= dfn[u]){
if(u != root||child > 1)cut[u]=1;///判断是否是割点
bcc[++bcc_cnt].clear();
for(;;){
EDGE x = s.top();s.pop();
if(bccno[x.u] != bcc_cnt){
bcc[bcc_cnt].pb(x.u); bccno[x.u] = bcc_cnt;
}
if(bccno[x.v] != bcc_cnt){
bcc[bcc_cnt].pb(x.v); bccno[x.v] = bcc_cnt;
}
if(x.u == u && x.v == v)break;
}
}
}else if(v != f && dfn[v] < dfn[u]){///有反向边
s.push(edge);
low[u] = min(low[u],dfn[v]);
}
}
}
void init(){
tot=2;
bcc_cnt=dfs_clock=0;
mst(head,-1);
mst(dfn,0);
mst(low,0);
mst(cut,0);
mst(bccno,0);
mst(odd,0);
while(!s.empty())s.pop();
}
强联通模板(包含 割顶,桥,双联通,有向图 )
#include <iostream>
#include <cstring>
#include <vector>
#include <stack>
#define mst(x,a) memset(x,a,sizeof(x))
#define fzhead EDGE(int _to, int _next)
#define fzbody to(_to), next(_next)
#define For(i,x,y) for(int i=(x); i<=(y); i++)
#define fori(i,x,y) fori(int i=(x); i<(y); i++)
using namespace std;
const int maxn=5000+10;
const int maxm=2e4+10;
struct EDGE{
int to, next;
EDGE(){}
fzhead:fzbody{}
}e[maxm];
int head[maxn], cut[maxn], vis[maxn],bcc[maxn],bcc_num[maxn],col[maxn];
int fa[maxn],pre[maxn], low[maxn], dfn[maxn],bcc_cnt,tot, dfs_clock;
int bridge[maxm],in[maxn];
void add(int bg, int to){
e[tot]=EDGE(to,head[bg]);
head[bg]=tot++;
}
stack<int>s;
void init(){
tot=2;
dfs_clock=bcc_cnt=0;
mst(head,-1);
mst(vis,0);
mst(bcc,0);
mst(fa,0);
mst(pre,0);
mst(low,0);
mst(dfn,0);
mst(bcc_num,0);
mst(col,0);
mst(cut,0);
mst(in,0);
while(!s.empty())s.pop();
}
int root;
void tarjan(int u, int f){
dfn[u]=low[u]=++dfs_clock;
s.push(u);
fa[u]=f;
vis[u]=1;
int child=0;
for(int i=head[u]; i!=-1; i=e[i].next){
int v=e[i].to;
if(!dfn[v]){
pre[v]=i^1;
child++;
tarjan(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]&&(u!=root||child>1))cut[u]=1;
}else if(v!=f&&dfn[v]<dfn[u])low[u]=min(low[u],dfn[v]);
///else if(vis[v])low[u]=min(low[u],dfn[v]);
//有向图,时才用这句话。
}
if(low[u]==dfn[u]){
col[u]=++bcc_cnt;
vis[u]=0;
int v;
bridge[pre[u]^1]=bridge[pre[u]]=1;
do{
v = s.top();s.pop();
col[v]=bcc_cnt;
bcc_num[bcc_cnt]++;
}while(v!=u);
}
}
int n,m;
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n>>m;
int u,v;
init();
For(i,1,m){
cin>>u>>v;
add(u,v);add(v,u);
}
For(u,1,n)if(!dfn[u])dfs_clock=0, root=u, tarjan(u,-1);
//cout<<bcc_cnt<<endl;
For(u,2,n){
int a=col[u],b=col[fa[u]];
if(a==b)continue;
in[a]++,in[b]++;
}
int ans=0;
For(i,1,bcc_cnt)if(in[i]==1)ans++;
//cout<<ans<<endl;
cout<<(ans+1)/2<<endl;
return 0;
}
Divide Groups HDU - 4751
本题就是裸的二分图染色。(本题的题意。假如互相认识,那么就随便染)
Wrestling Match HDU - 5971
- 由于题目已经有事先分配的两个集合。所以先进行两次dfs。
- 最后再进行一次常规dfs即可
1.模板题(tarjan)
Network of Schools POJ - 1236
模板题(对于ans2,可以转换为DAG中加最少条边,使得DAG变为强联通图)
信息传递 LibreOJ - 2421
在求tarjan时,把每个环的长度取一个min
Road Construction POJ - 3352
low用来缩点。
2.割点
Network UVA - 315
求割点的模板
3. 桥
Critical Links UVA - 796
4. 双联通分量
Redundant Paths POJ - 3177
求完双联通后,要思考一下。
5. 点-双连通分量(无向图)
Knights of the Round Table UVA - 1364
一道挺有意思的,里面有奇圈的证明。(也是这一道让我知道了无向图的tarjan和有向图tarjan的区别)
Mining Your Own Business UVA - 1108
求 点双连通 涉及 一点组合知识
6. 强连通分量 (有向图)
- 一般求一次强连通分量,之后缩点
Proving Equivalences UVA - 12167
本题就是求完后,进行一次缩点即可。之后思维了
The Largest Clique UVA - 11324
先求强连通分量,之后缩点,在DAG上进行简单dp即可(求一个最长路)。
Calling Circles UVA - 247
本题是求tarjan的强连通分量的模板题。之后输出每个强连通分量(不要求顺序)
7. 2-sat
问题
主要就是问题的转换,之后写出 或运算(a
∨
\lor
∨ b),或运算即为限制条件。
关键
:
找到限制条件,根据 子句(条件来定义 边的个数)
D F S DFS DFS染色法的模板
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>
#include <cmath>
#define pb push_back
#define fzhead EDGE(int _to, int _next)
#define fzbody to(_to), next(_next)
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(int i=(x); i<=(y); i++)
#define fori(i,x,y) for(int i=(x); i<(y); i++)
using namespace std;
const int maxn=2000+10;
const int maxm=2000*2000+10;
struct EDGE{
int to,next;
EDGE(){}
fzhead:fzbody{}
}e[2*maxm*2];
int head[2*maxn],tot;
bool mark[maxn*2];
///int s
stack<int>s;
void add(int bg, int to){
e[tot]=EDGE(to,head[bg]);
head[bg]=tot++;
}
void add_clause(int x, int xval, int y, int yval){
x=x*2+xval;
y=y*2+yval;
add(x^1,y);
add(y^1,x);
}
bool dfs(int x){
if(mark[x^1])return false;
if(mark[x])return true;
mark[x]=true;
s.push(x);
for(int i=head[x]; i!=-1; i=e[i].next){
int v=e[i].to;
if(!dfs(v))return false;
}
return true;
}
int n;
bool twoset(){
for(int i=0; i<2*n; i+=2){//fori(i,0,2*n){
if(!mark[i] && !mark[i+1]){
while(!s.empty())s.pop();
if(!dfs(i)){
while(!s.empty())mark[s.top()] = false,s.pop();
if(!dfs(i+1))return false;
}
}
}
return true;
}
void init(){
mst(mark,0);tot=2;
mst(head,-1);
while(!s.empty())s.pop();
}
Now or later UVA - 1146
二分 + 2-sat。
Astronauts UVA - 1391
2 - sat