1 tarjan算法的应用
tarjan算法中
dfn[] :每个点的dfs序
low[]:每个点能到达的dfs序最小的节点的dfs序
1.将强连通分量缩点变成DAG
技巧:
(1)将某个DAG补成强连通图 需要加的边为 max(入度,出度).
(2)做完 Tarjan 算法后,按编号递减的顺序就是拓扑序.
int scc_cnt,id[N],siz[N];
int stk[N],top;
bool instk[N];
int w[N],sum[N];
void tarjan(int x){
dfn[x] = low[x] = ++ timestamol;
stk[++top] = x,instk[x] = 1;
for(int i = h[x];i;i = ne[i]){
int y = e[i];
if(!dfn[y]){//y点未被遍历过
tarjan(y);
low[x] = min(low[x],low[y]);//计算能达到的最小时间戳
}
//y点在栈中 说明还没出栈 是dfs序比当前点y小的
//则其 1要么是横插边(左边分支的点)
// 2要么是y的祖宗节点
else if(instk[y]) low[x] = min(low[x],dfn[y]); //栈代表当前未被搜完的强连通分量的所有点 , 在同一scc中 可以取min
}
if(dfn[x] == low[x]){//x是这个scc的最高点,把这个scc缩点
scc_cnt++;
int y;
do{
y = stk[top--];
instk[y] = 0;
id[y] = scc_cnt;
}while(x != y);
}
}
2.求割点
bool cut[N];
void tarjan(int x,int pr){
dfn[x] = low[x] = ++timestamp;
int child = 0; //子节点
for(int i = h[x];i;i = ne[i]){ //遍历每一个相邻的点
int y = e[i];
if(!dfn[y]){//这个点没有被遍历过
tarjan(y,pr);
low[x] = min(low[x],low[y]);
if(low[y] >= dfn[x] && x != pr){ //当前节点的这个子节点dfs序大于等于当前节点的dfs序 且不是连通块第一个访问的节点
cut[x] = 1;
}
if(x == pr) child++;
}
low[x] = min(low[x],dfn[y]);
}
if(child >= 2 && x == pr){//开始的第一个点就是割点 特判
cut[x] = 1;
}
}
3.求桥和边的双连通分量
bool bridge[M];
void tarjan(int x,int from){ //from 是边
dfn[x] = low[x] = ++timestamp;
stk[++top] = x;
for(int i = h[x];i;i = ne[i]){ //i是边
int y = e[i]; // y是x的临点
if(!dfn[y]){
tarjan(y,i);
low[x] = min(low[x],low[y]);
if(dfn[x] < low[y]){
bridge[i] = bridge[i ^ 1] = 1; //成对变换
}
}
else if(i != (from ^ 1)){
low[x] = min(low[x],dfn[y]);
}
}
if(dfn[x] == low[x]){
dcc_cnt++;
int y;
do{
y = stk[top--];
id[y] = dcc_cnt;
}while(y != x);
}
}
2二分图
1.染色法求二分图
一个图是二分图 <=> 图中不存在奇数环 <=> 染色法过程中不存在矛盾
bool dfs(int x,int c,int mid){
color[x] = c;
for(int i = h[x];i;i = ne[i]){
int y = e[i];
if(w[i] <= mid) continue; // 在最大范围内进行染色
if(color[y]){
if(color[y] == c) return 0; //有相同染色不成立
}
else if(!dfs(y,3 - c,mid)) return 0; // 不是异色不成立
}
return 1; //成立了
}
2.求二分图最大匹配
匈牙利算法 时间复杂度O(mn)
bool vis[N];
int match[N];
bool dfs(int x){
for(int i = h[x];i;i = ne[i]){
int y = e[i];
if(!vis[y]){
vis[y] = 1;
if(!match[y] || dfs(match[y])){
match[y] = x;
return 1;
}
}
}
return 0;
}
for(int i = 1;i<=n;i++){
memset(vis,0,sizeof(vis));
if(dfs(i)) ans++;
}
二分图匹配的模型有两个要素
1.节点能分为两个集合 , 每个集合内部有0条边
2.每个节点只能与一条匹配边相连
3其他的一些
求取模的组合数
const int mod = 998244353;
int pow(int x,int n){
int ans = 1;
while(n){
if(n % 2)ans = ans*x%mod;
x = x*x%mod;
n /= 2;
}
return ans;
}
int C(int n,int m){
int up = 1,dn = 1;
for(int i = 1;i <= m;++i){
up = up*(n+1-i)%mod;
dn = dn*i%mod;
}
return up*pow(dn,mod-2)%mod;
}
构造两两不互质的一组数,最常见的有两种:
- 2 x 3 ,3 x 5,5 x 7,7 x 9,…,
- 2 x 1,2 x 2,2 x 3,2 x 4 …