连通性
##有向图强连通缩点
####所需数组
i d x , l o w [ M A X N ] , d f n [ M A X N ] idx,low[MAXN],dfn[MAXN] idx,low[MAXN],dfn[MAXN]------------时间戳和两个访问标志
s c c n o , s c c [ M A X N ] sccno ,scc[MAXN] sccno,scc[MAXN] ————点属于哪个连通分支,及连通分量数量
s t a c k < i n t > s t , i n s t [ M A X N ] stack<int> st ,inst[MAXN] stack<int>st,inst[MAXN]————栈,一个中间工具,用于分辨点属于哪个连通分量
v e c t o r < i n t > G [ M A X N ] ; vector<int> G[MAXN]; vector<int>G[MAXN];————图
const int N = 40000;
const int M = 500000;
int head[N];
int top;
int n,m;
struct Edge
{
int val;
int to;
int next;
}edge[M];
void addedge(int a,int b,int c)
{
edge[top].to=b;
edge[top].val=c;
edge[top].next=head[a];
head[a]=top++;
}
void init()
{
top=0;
for (int i=1;i<=n;i++) head[i]=-1;
}
int idx,low[N],dfn[N];
int sccno,scc[N];
stack<int>st; int inst[N];
void tarjan_init(){
init();
clr(dfn,0);
clr(inst,0);
clr(scc,0);
sccno=idx=0;
while(!st.empty()) st.pop();
}
void tarjan(int x)
{
low[x]=dfn[x]=++idx;
st.push(x);
inst[x]=1;
for(int i=head[x];~i;i=edge[i].next)
{
int v=edge[i].to;
if(!dfn[v]){
tarjan(v);
low[x]=min(low[v],low[x]);
}
else if(inst[v]){
low[x]=min(low[x],dfn[v]);
}
}
if(low[x]==dfn[x]){
++sccno;
int v;
while(1){
v=st.top();
st.pop();
inst[v]=0;
scc[v] = sccno;
if(x==v) break;
}
}
}
##割边&&割点
//割点
const int N = 40000;
const int M = 500000;
int n,m;
int head[N];
int top;
struct Edge{
int val;
int to;
int next;
}edge[M];
void addedge(int a,int b,int c)
{
edge[top].to=b;
edge[top].val=c;
edge[top].next=head[a];
head[a]=top++;
}
void init()
{
top=0;
for (int i=1;i<=n;i++) head[i]=-1;
}
int low[N],dfn[N],idx;
int iscut[N];
int rootson;
void tarjan_init(){
init();
clr(dfn,0);
idx=rootson=0;
clr(iscut,0);
}
void tarjan(int u,int pre,int id){
dfn[u]=low[u]=++idx;
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].to;
if(i==(id^1)) continue;
if(!dfn[v]){
tarjan(v,u,i);
if(u==1) rootson++;
else {
low[u]=min(low[u],low[v]);
if(low[v]>=dfn[u]) iscut[u]=1;
}
}else{
low[u]= min(low[u],dfn[v]);
}
}
}
if(rootson>1) iscut[1]=1;//调用后 加一句
//割边
const int N = 40000;
const int M = 500000;
int n,m;
int head[N];
int top;
struct Edge{
int val;
int to;
int next;
}edge[M];
void addedge(int a,int b,int c){
edge[top].to=b;
edge[top].val=c;
edge[top].next=head[a];
head[a]=top++;
}
void init()
{
top=0;
clr(head,-1);
}
int low[N],dfn[N],idx;
int fa[N],isbridge[N];
int bridge;
void tarjan_init()
{
init();
clr(dfn,0);
bridge=idx=0;
clr(isbridge,0);
clr(fa,0);
}
void tarjan(int u,int pre,int id){
fa[u]=pre;
low[u]=dfn[u]=++idx;
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].to;
if(i==(id^1)) continue; //......
if(!dfn[v]){
tarjan(v,u,i);
low[u]=min(low[u],low[v]);
if(low[v] > dfn[u]) {
isbridge[v]=1,bridge++;
//cout<<"isbridge"<<endl;
}
}
else if(v!=pre){ //。。。。。
low[u]=min(low[u],dfn[v]);
}
}
}
//注意isbridge数组与fa数组一起用,且若isbridge[i],则i和fa[i]是割边
//重边要不要注意写法
割边割点注意=号
最短路
单源最短路dij
O
(
(
E
+
N
)
l
o
g
N
)
O((E+N)logN)
O((E+N)logN)
const int N=3000;
const int M=71000;
int n,m;
int dis[N];
int vis[N];
struct edge
{
int to,v;
};
struct qnode
{
int id;
int v;
bool operator <(const qnode &r)const
{
return v>r.v;
}
};
vector<edge> G[N];
void addedge(int u,int v,int w){
G[u].push_back(edge{v,w});
}
void init(){
for(int i=0;i<=n;i++) G[i].clear();
}
void Dij(int start)
{
clr(vis,0);
clr(dis,INF);
priority_queue<qnode> q;
dis[start] = 0;
q.push(qnode{start,0});
while(!q.empty()){
qnode cur=q.top();
q.pop();
int u=cur.id;
if(vis[u]) continue;
vis[u]=1;
for(int i=0;i<G[u].size();i++){
int v=G[u][i].to,w=G[u][i].v;
if(!vis[v]&&dis[v]>dis[u]+w){
dis[v]=dis[u]+w;
q.push(qnode{v,dis[v]});
}
}
}
}
//前向星模板 防止类似poj老oj卡vector 一般不用
const int N=40010;
const int M=150010;
int head[N];
int top;
int n,m;
struct Edge
{
int val;
int to;
int next;
}edge[M];
void addedge(int a,int b,int c)
{
edge[top].to=b;
edge[top].val=c;
edge[top].next=head[a];
head[a]=top++;
}
void init()
{
top=0;
clr(head,-1);
}
int dis[N];
int vis[N];
struct qnode
{
int id;
int dis;
bool operator <(const qnode &r)const
{
return dis>r.dis;
}
};
void Dij(int start)
{
clr(vis,0);
clr(dis,INF);
priority_queue<qnode> q;
dis[start] = 0;
q.push(qnode{start,0});
while(!q.empty()){
qnode cur=q.top();
q.pop();
int u=cur.id;
if(vis[u]) continue;
vis[u]=1;
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].to,w=edge[i].val;
if(!vis[v]&&dis[v] > w+dis[u]){
dis[v] = w+dis[u];
q.push(qnode{ v,dis[v]});
}
}
}
}
spfa
O
(
k
m
)
O(km)
O(km) k最坏情况下是n
const int N=10010;
const int M=500010;
int n,m;
int dis[N];
struct edge{
int to;int v;
};
vector<edge> G[N];
int vis[N];
int cnt[N];//每个点的入队次数
void init(){
for(int i=0;i<=n;i++) G[i].clear();
}
void addedge(int u,int v,int w){
G[u].push_back(edge{v,w});
}
int spfa(int start){
clr(vis,0);
clr(dis,INF);
queue<int> q;
dis[start] = 0;
//vis[start] = 1;
clr(cnt,0);
cnt[start] = 1;
q.push(start);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0; //不在队列里了
for(int i=0;i<G[u].size();i++){
int v=G[u][i].to;
int cost=G[u][i].v;
if(dis[v]>dis[u]+cost)
{
dis[v]=dis[u]+cost;
if(!vis[v]){
vis[v] = 1;
q.push(v);
if(++cnt[v]>n) return 0;
}
}
}
}
return 1;
}
前向星
const int N=50010;
const int M=50010;
int head[N];
int top;
int n,m;
struct Edge
{
int val;
int to;
int next;
}edge[M];
void addedge(int a,int b,int c)
{
edge[top].to=b;
edge[top].val=c;
edge[top].next=head[a];
head[a]=top++;
}
void init()
{
top=0;
clr(head,-1);
}
int dis[N];
int vis[N];
int cnt[N];//每个点的入队次数
int spfa(int start){
clr(vis,0);
clr(dis,INF);
queue<int> q;
dis[start] = 0;
//vis[start] = 1;
clr(cnt,0);
cnt[start] = 1;
q.push(start);
while(!q.empty())
{
int u=q.front();
q.pop();
vis[u]=0; //不在队列里了
for(int i=head[u];~i;i=edge[i].next){
int v=edge[i].to;
int cost=edge[i].val;
if(dis[v]>dis[u]+cost)
{
dis[v]=dis[u]+cost;
if(!vis[v]){
vis[v] = 1;
q.push(v);
if(++cnt[v]>n) return 0;
}
}
}
}
return 1;
}
//读入优化 哎 poj
inline LL read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
差分约束
给定一串序列,长度为n。
a
1
,
a
2
,
a
3
.
.
.
.
.
.
a
n
a_1,a_2,a_3......a_n
a1,a2,a3......an
并给定m的限制条件,条件的格式为
a
i
−
a
j
<
=
c
a_i-a_j<=c
ai−aj<=c
求
a
n
−
a
1
a_n-a_1
an−a1的最大值
考虑序列三个数a,b,c
a
−
b
<
=
v
1
a-b<=v_1
a−b<=v1
b
−
c
<
=
v
2
b-c<=v_2
b−c<=v2
a
−
c
<
=
v
3
a-c<=v_3
a−c<=v3
⟹
a
−
c
<
=
m
i
n
(
v
3
,
(
v
1
+
v
2
)
)
\Longrightarrow a-c<=min(v_3,(v_1+v_2))
⟹a−c<=min(v3,(v1+v2))
这个缩小约束条件的过程就是求最短路的过程
所以可以用最短路模型来解决
模型1.给定一串序列
a
1
−
a
n
a_1 - a_n
a1−an 并给出若干限制条件
a
i
−
a
j
<
=
c
a_i - a_j<=c
ai−aj<=c,要求
a
k
1
a_{k1}
ak1 与
a
k
2
a_{k2}
ak2 的最大可能差值?
方法:对于每个限制条件
a
i
−
a
j
<
=
c
a_i - a_j<=c
ai−aj<=c,从点j到点i建立一条边©,求
a
k
1
a_{k1}
ak1 到
a
k
2
a_{k2}
ak2 的最短路即可。
推广
a
k
1
a_{k1}
ak1 与
a
k
2
a_{k2}
ak2 的最小可能差值?
就是反一反
a
−
b
>
=
v
1
a-b>=v_1
a−b>=v1
b
−
c
>
=
v
2
b-c>=v_2
b−c>=v2
a
−
c
>
=
v
3
a-c>=v_3
a−c>=v3
⟹
a
−
c
>
=
m
a
x
(
v
3
,
(
v
1
+
v
2
)
)
\Longrightarrow a-c>=max(v_3,(v_1+v_2))
⟹a−c>=max(v3,(v1+v2))
要满足所有条件,(a-c的)范围扩大,求最长路
模型2.给定一串序列
a
1
−
a
n
a_1 - a_n
a1−an 并给出若干限制条件
a
i
−
a
j
>
=
c
a_i - a_j>=c
ai−aj>=c,要求
a
k
1
a_{k1}
ak1 与
a
k
2
a_{k2}
ak2 的最小可能差值?
方法:对于每个限制条件
a
i
−
a
j
>
=
c
a_i - a_j>=c
ai−aj>=c,从点j到点i建立一条边©,求
a
k
2
a_{k2}
ak2 到
a
k
1
a_{k1}
ak1 的最长路即可。
条件转换:
a
−
b
<
=
c
⟹
b
−
a
>
=
c
a-b<=c \Longrightarrow b-a>=c
a−b<=c⟹b−a>=c
a
−
b
=
c
⟹
a
−
b
<
=
c
,
b
−
a
<
=
c
a-b=c \Longrightarrow a-b<=c , b-a<=c
a−b=c⟹a−b<=c,b−a<=c
…
(简单数学变换)
模型2也可转换为模型1
a
−
b
>
=
v
1
a-b>=v_1
a−b>=v1 ``_````
b
−
a
<
=
−
v
1
b-a<=-v_1
b−a<=−v1
b
−
c
>
=
v
2
⟹
b-c>=v_2 \Longrightarrow
b−c>=v2⟹
c
−
b
<
=
−
v
2
c-b<=-v_2
c−b<=−v2
a
−
c
>
=
v
3
a-c>=v_3
a−c>=v3 ```````
c
−
a
<
=
−
v
3
c-a<=-v_3
c−a<=−v3
求
a
k
2
a_{k2}
ak2 到
a
k
1
a_{k1}
ak1 的最短路
模型2优化.给定一串序列
a
1
−
a
n
a_1 - a_n
a1−an 并给出若干限制条件
a
i
−
a
j
>
=
c
a_i - a_j>=c
ai−aj>=c,要求
a
k
1
a_{k1}
ak1 与
a
k
2
a_{k2}
ak2 的最小可能差值?
方法:对于每个限制条件
a
j
−
a
i
<
=
−
c
a_j - a_i<=-c
aj−ai<=−c,从点i到点j建立一条边(-c),求
a
k
1
a_{k1}
ak1 到
a
k
2
a_{k2}
ak2 的最短路即可。
对于一个求最短路(最长路)约束条件有可能有三种情况:
1.有上界,即有解
2.无解(有负环)
3.任意多的解(约束条件不够强,或者说图不强连通)
有解就是最短路
无解就是有负环,最短路负无穷大
无穷解就是不连通,最短路正无穷大
题目集:
最长路(模板题)
https://blog.csdn.net/wyxxzsy/article/details/82777158
再提一句,超级源点,一开始对于网上加入超级源点,直接计算最短路很不理解,因为加入超级源点之后,源点到每个点的距离至多是0,所以我一度认为,加入超级源点只是为了图的连通性,判断负环而已。
但在最长路模型里不一样,它的权值都是负的,正确的权值是-dis[v],0到反而是最小值(加负号后)了,不会影响答案,有意思。
2-sat
拆点+建模
int low[N<<1],dfn[N<<1],idx;
stack<int> st;int inst[N<<1];
int scc[N<<1],sccno;
vector<int> G[N<<1];
int n;
void init()
{
for(int i=0;i<=2*n;i++) G[i].clear();
while(!st.empty()) st.pop();
clr(dfn,0);
clr(inst,0);
idx=sccno=0;
}
void tarjan(int x){
low[x] = dfn[x]=++idx;
st.push(x);
inst[x]=1;
for(int i=0;i<G[x].size();i++)
{
int v=G[x][i];
if(!dfn[v]){
tarjan(v);
low[x] = min(low[v],low[x]);
}
else if(inst[v]){
low[x]= min(low[x],dfn[v]);
}
}
if(low[x]==dfn[x]){
++sccno;
int v;
do{
v=st.top();
st.pop();
inst[v]=0;
scc[v]=sccno;
}while(x!=v);
}
}
bool solve(){
for(int i=0;i<2*n;i++)
if(!dfn[i]) tarjan(i);
for(int i=0;i<2*n;i+=2)
{
if(scc[i]==scc[i^1]) return false;
}
return true;
}
网络流
const int MAXN = 100010;//点数的最大值
const int MAXM = 400010;//边数的最大值
const int INF = 0x3f3f3f3f;
struct Edge
{
int to,next,cap,flow;
} edge[MAXM]; //注意是MAXM
int tol;
int head[MAXN];
int gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN];
void init()
{
tol = 0;
memset(head,-1,sizeof(head));
}
//加边,单向图三个参数,双向图四个参数
void addedge(int u,int v,int w,int rw = 0)
{
edge[tol] = Edge{v,head[u],w,0};
head[u] = tol++;
edge[tol] =Edge{u,head[v],rw,0};
head[v] = tol++;
}
//输入参数:起点、终点、点的总数
//点的编号没有影响,只要输入点的总数
int sap(int start,int end,int N)
{
memset(gap,0,sizeof(gap));
memset(dep,0,sizeof(dep));
memcpy(cur,head,sizeof(head));
int u = start;
pre[u] = -1;
gap[0] = N;
int ans = 0;
while(dep[start] < N)
{
if(u == end)
{
int Min = INF;
for(int i = pre[u]; i != -1; i = pre[edge[i^1].to])
if(Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap - edge[i].flow;
for(int i = pre[u]; i != -1; i = pre[edge[i^1].to])
{
edge[i].flow += Min;
edge[i^1].flow -= Min;
}
u = start;
ans += Min;
continue;
}
bool flag = false;
int v;
for(int i = cur[u]; i != -1; i = edge[i].next)
{
v = edge[i].to;
if(edge[i].cap - edge[i].flow && dep[v]+1 == dep[u])
{
flag = true;
cur[u] = pre[v] = i;
break;
}
}
if(flag)
{
u = v;
continue;
}
int Min = N;
for(int i = head[u]; i != -1; i = edge[i].next)
if(edge[i].cap - edge[i].flow && dep[edge[i].to] < Min)
{
Min = dep[edge[i].to];
cur[u] = i;
}
gap[dep[u]]--;
if(!gap[dep[u]])return ans;
dep[u] = Min+1;
gap[dep[u]]++;
if(u != start) u = edge[pre[u]^1].to;
}
return ans;
}
LCA
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10050;
int rmq[maxn * 2];
struct ST {
int mm[2 * maxn];
int dp[2 * maxn][20];
void init(int n) {
mm[0] = -1;
for(int i = 1; i <= n; i++) {
mm[i] = ((i & (i - 1)) == 0) ? mm[i - 1] + 1 : mm[i - 1];
dp[i][0] = i;
}
for(int j = 1; j <= mm[n]; j++) {
for(int i = 1; i + (1 << j) - 1 <= n; i++) {
dp[i][j] = rmq[dp[i][j - 1]] < rmq[dp[i + (1 << (j - 1))][j - 1]] ?
dp[i][j - 1] : dp[i + (1 << (j - 1))][j - 1];
}
}
}
int query(int a, int b) {
if(a > b) swap(a, b);
int k = mm[b - a + 1];
return rmq[dp[a][k]] <= rmq[dp[b - (1 << k) + 1][k]] ? dp[a][k] : dp[b - (1 << k) + 1][k];
}
} st;
vector<int> g[1005];
int tot;
int F[maxn * 2];//欧拉序
int P[maxn * 2];//节点i在F中第一次出现的位置
bool used[maxn];
void init() {
for(int i = 0; i < 2000; i++)
g[i].clear();
memset(used, 0, sizeof(used));
}
void dfs(int x, int pre, int dep) {
F[++tot] = x;
rmq[tot] = dep;
P[x] = tot;
for(int i = 0; i < g[x].size(); i++) {
int s = g[x][i];
if(s != pre) {
dfs(s, x, dep + 1);
F[++tot] = x;
rmq[tot] = dep;
}
}
}
void LCA_init(int root, int n) {
tot = 0;
dfs(root, root, 0);
st.init(2 * n - 1);
}
int lca(int u, int v) {
return F[st.query(P[u], P[v])];
}
int main() {
int t;
cin >> t;
int k = 1;
while(t--) {
init();
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i++) {
int m;
scanf("%d", &m);
if(m == 0) continue;
for(int j = 1; j <= m; j++) {
int a;
scanf("%d", &a);
g[i].push_back(a);
g[a].push_back(i);
used[a] = true;
}
}
int q;
scanf("%d", &q);
printf("Case %d:\n", k++);
int root;
for(int i = 1; i <= n; i++) {
if(!used[i]) {
root = i;
break;
}
}
LCA_init(root, n);
while(q--) {
int u, v;
scanf("%d%d", &u, &v);
printf("%d\n", lca(u, v));
}
}
return 0;
}