ST表(RMQ问题)
//打表与查询 这个是针对最大最小值
void STpre()
{
for(int i = 1; i <= n; i++) dp[i][0][0] = dp[i][0][1] = d[i];
for(int j = 1; (1<<j) < n; j++)
for(int i = 1; i <= n; i++)
{
if(i + (1<<j) -1 > n) continue;
dp[i][j][0] = max(dp[i][j-1][0],dp[i+(1<<(j-1))][j-1][0]);
dp[i][j][1] = min(dp[i][j-1][1],dp[i+(1<<(j-1))][j-1][1]);
}
}
void rmq(int a,int b)
{
int len = b-a+1;
int k = 0;
while(1<<(k+1) <= len) k++;
int la = max(dp[a][k][0],dp[b-(1<<k)+1][k][0]);
int sm = min(dp[a][k][1],dp[b-(1<<k)+1][k][1]);
}
LCA(tarjan)
void tarjan(int u)
{
anc[u] = u;
for(int i = h[u] ; i != -1 ; i = edge[i].next)
{
int v = edge[i].to;
if(!vis[v])
{
vis[v] = 1;
tarjan(v); join(u,v);
anc[getf(u)] = u;
vis[v] = 0;
}
}
col[u] = 1;
for(int i = h1[u]; ~i; i = query[i].next)
{
int v = query[i].to, id = query[i].w;
if(!col[v]) continue;
ans[id] = d[u] + d[v] - 2*d[anc[getf(v)]];
}
}
LCA(倍增)
预处理每个结点的父亲和深度
void pre(int u,int d,int fa) //处理每个结点的深度和父亲
{
for(int i = h[u]; ~i; i = edge[i].next)
{
int v = edge[i].to, w = edge[i].w;
if(fa != v)
{
dis[v] = dis[u] + w;
dp[v][0] = u; maxd[v] = d;
pre(v,d+1,u);
}
}
}
倍增的预处理
int doubl() //倍增的预处理
{
for(int j = 1; j <= 20; j++)
for(int i = 1; i <= n ; i++)
{
if((1 << j) > maxd[i]) continue;
int k = dp[i][j-1]; dp[i][j] = dp[k][j-1];
}
return 0;
}
询问
int query(int a,int b)
{
int x = a, y = b;
int ans = 0;
if(maxd[x] > maxd[y]) swap(x,y);
for(int j = 20; j >= 0; j--)//先跳到同一高度,再一起往上跳。
{
if(maxd[x] == maxd[y]) break;
if(maxd[y] - (1<<j) < maxd[x]) continue;
ans += (dis[y] - dis[dp[y][j]]);
y = dp[y][j];
}
if(x == y) return ans;
for(int j = 20; j >= 0; j--)
{
if(dp[x][0] == dp[y][0] )break;
if(maxd[y] - (1 << j) < 0 || dp[x][j] == dp[y][j]) continue;
ans += (dis[x]-dis[dp[x][j]] + dis[y] - dis[dp[y][j]]);
x = dp[x][j], y = dp[y][j];
}
ans += (dis[x] - dis[dp[x][0]] + dis[y] - dis[dp[y][0]]);
return ans;
}
tarjan
(1).求有向图强连通分量
int tarjan(int id)
{
vis[id] = 1;
dfn[id] = low[id] = ++tot;
sta[index++] = id;
for(int i = head[id]; ~i; i = edge[i].next)
{
int v = edge[i].to;
if(!dfn[v])
{
tarjan(v);
low[id] = min(low[id],low[v]);
}
else if(vis[v])
low[id] = min(low[id],dfn[v]);
}
if(low[id] == dfn[id])
{
int num = 0;
do
{
vis[sta[index--] ] = 0;
num++;
}while(sta[index] != id);
ans[anum ++] = num;
}
return 0;
}
(2).求无向图割点
void tarjan(int u,int father)
{
dfn[u] = low[u] = ++tot;
int child = 0;
for(int i = h[u]; ~i ; i = edge[i].next)
{
int v = edge[i].to;
if(!dfn[v])
{
child++;
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(low[v] >= dfn[u]) isCut[u] = 1;
}
else if(dfn[v] < dfn[u] && father != v)
low[u] = min(low[u],dfn[v]);
}
if(father == -1 && child == 1) isCut[u] = 0;
}
(3).求无向图割边(注意割边需要去重,也就是有重边的都不是割边)
int tarjan(int u,int fa)
{
dfn[u] = low[u] = ++tot;
for(int i = h[u]; ~i; i = edge[i].next)
{
int v = edge[i].to;
if(!dfn[v])
{
tarjan(v,u);
low[u] = min(low[u],low[v]);
if(dfn[u] < low[v])
{
int a = u, b = v;
if(u > v) swap(a,b);
edge[i].isb = 1;
}
}
else if(dfn[v] < dfn[u] && v != fa)
low[u] = min(low[u],dfn[v]);
}
return 0;
}
匈牙利算法:
bool Find(int u)
{
for(int i = 1; i < rnum; i++)
if(g[u][i] && !used[i])
{
used[i] = 1;
if(belong[i] == -1 || Find(belong[i]))
{
belong[i] = u;
return true;
}
}
return false;
}
2-sat问题
缩点 + 拓扑序
void tarjan(int u)
{
low[u] = dfn[u] = ++tot;
S[++snum] = u; inS[u] = 1;
for(int i = h[u]; ~i; i = edge[i].next)
{
int v = edge[i].to;
if(!dfn[v])
{
tarjan(v);
low[u] = min(low[u],low[v]);
}
else if(inS[v])
low[u] = min(low[u],dfn[v]);
}
if(low[u] == dfn[u])
{
int v = -1;
snum++;
while(v != u)
{
v = S[snum--];
scc[v] = snum; inS[v] = 0;
}
}
}
void solve()
{
for(int i = 0; i < 2*n; i++)
if(!dfn[i]) tarjan(i);
int flag = 0;
for(int i = 0; i < 2*n; i += 2)
if(scc[i] == scc[i+1]) {flag = 1; break;}
flag == 0 ? printf("YES\n") : printf("NO\n");
}
Dinic求最大流 前向弧优化+ 单路增广(因为多路我很迷茫)
bfs求层次图
int bfs()
{
for(int i = s; i <= t; i++)
deg[i] = -1;
queue<int> q;
q.push(s); deg[s] = 0;
while(!q.empty())
{
int u = q.front(); q.pop();
for(int i = h[u]; ~i ; i = edge[i].next)
{
int v = edge[i].to,cap = edge[i].cap;
if(cap && deg[v] == -1)
{
deg[v] = deg[u] + 1;
q.push(v);
}
}
}
return deg[t] != -1;
}
dfs找增广路
int dfs(int u,int f)
{
if(u == t) return f;
for(int i = cur[u]; ~i; i = edge[i].next)
{
cur[u] = i;
int v = edge[i].to, cap = edge[i].cap, rev = edge[i].rev;
if(cap && deg[v] == deg[u] + 1)
{
int k = dfs(v,min(f,cap));
if(!k)continue;
edge[i].cap -= k;
edge[rev].cap += k;
return k;
}
}
return 0;
}
void solve()
{
int flow = 0;
while(bfs())
{
for(int i = s; i <= t; i++) cur[i] = h[i];
while(int k = dfs(s,inf)) flow += k;
}
cout << m-flow << endl;
}
E-K + bellman
E-k算法其实就是找增广路的bfs实现,来达到每次都找的是最短增广路的目的。
对于最小费用最大流其实就是每次找费用最少且路径最短的增广路。
int bfs()
{
for(int i = 0; i < maxn; i++)
d[i] = inf,a[i] = 0,inq[i] = 0;
queue<int> q;
q.push(0);
d[0] = 0;a[0] = inf;
inq[0] = 1;
while(!q.empty()) //找最短且费用最少的增广路
{
int u = q.front(); q.pop();
inq[u] = 0;
for(int i = h[u]; ~i; i = edge[i].next)
{
int v = edge[i].to,cap = edge[i].cap, w = edge[i].w;
if(cap && d[v] > d[u] + w)
{
a[v] = min(a[u],cap); d[v] = d[u] + w;
pre[v] = i;
if(!inq[v]) {q.push(v); inq[v] = 1;}
}
}
}
if(d[t] == inf ) return 0;
for(int u = t; u != st; u = edge[pre[u]].from)
{
int rev = edge[pre[u]].rev;
edge[pre[u]].cap -= a[t];
edge[rev].cap += a[t];
}
return d[t];
}