tarjanあなたのノートを確認
(についてtarjan
の測定値、エレガント少し読書Tayang、地球にダウン少し読書尖塔)
0通信コンポーネント
有向グラフ:
強連結成分(SCC)Geshaあります
2つの点がお互いに到達する一つのプロットがあり、その後、同じ強連結成分の2点
の偉大な強連結成分は、最大の強連結成分です。
無向グラフ:
ユニコム全体サブグラフは、通信コンポーネントです。
請求が使用される一時tarjan 側通信コンポーネントビス(E-DCC)と部品ビス接続点(V-DCC)
ビス側通信コンポーネント(E-DCC)
図1の通信サブビス側の構成要素であることは、次にブリッジを意味します。
いいえコンポーネントは、この最大図の両面が接続部分グラフの各無向グラフの二重通信と呼ばれません。
ビスポイント通信コンポーネント(V-DCC)
無向グラフの場合、その内部の点のいずれかとの間の点の集合と、少なくとも二つのパスポイントが完全に重なっていない、これは二重通信コンポーネントによって画像内の点の集合です。
いいえツイン次連結成分ませんレビューになります
強連結成分tarjan
これはtarjan強連結成分と呼ばれる - ポイントを凝縮します。
片方向リンクのサブグラフについて、私たちは出発点を得るであろう検索ツリー、
これはサイドツリーを検索することができます唯一のサブグラフではありません。
(ツリー探索木の短側縁、非探索木の下ではなく、以下に遭遇ツリーの非エッジ側と称します)
スタックポイントまで検索の順番。
現在のポイント場合、我々は、非ツリーエッジを見つけ、それはその時点からこの時点までの間に強い連結成分の一部であるスタックにポイントの前に。
しかし、どのように我々はそれの最大の強連結成分のものを見つけるのですか?
私たちは、検索順序設定dfn[]
とトレーサビリティ値をlow[]
、その程度の現在のポイントlow[]
缶に最も近い点を取るdfn[]
か、自分dfn[]
。
この場合dfn[]!=low[]
、それは強く、次の点に現在地点から内部の部品に接続されているスタックです。
このような要件は、最大の強連結成分であることが証明されなければなりません。
コードの実装([テンプレート]):
#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=10086;
//forward star node
struct edge
{
int to,next;
} Edge[50086];
int cnt=1,head[maxn];
//forward star add edges
inline void add_edge(int from,int to)
{
Edge[cnt]=(edge){to,head[from]};
head[from]=cnt++;
}
//tarjan uses
int dfn[maxn],low[maxn],stack[maxn],now,top;
bool vst[maxn];
//After tarjan these point will put into baskets(group)
int group[maxn],gcnt;
void tarjan(int n)
{
dfn[n]=low[n]=++now;
stack[++top]=n;
vst[n]=true;
for (int i=head[n];i;i=Edge[i].next)
{
int to=Edge[i].to;
if (dfn[to]==0)
{
tarjan(to);
low[n]=min(low[n],low[to]);
}
else
{
if (vst[to])
{
low[n]=min(low[n],low[to]);
}
}
}
if (low[n]==dfn[n])
{
group[n]=++gcnt;
while (stack[top]!=n)
{
vst[stack[top]]=false;
group[stack[top--]]=gcnt;
}
//make n get out of stack
vst[n]=false;
top--;
}
}
int main()
{
int n,m;
cin>>n>>m;
for (register int i=1;i<=m;i++)
{
int f,t;
scanf("%d%d",&f,&t);
add_edge(f,t);
}
for (register int i=1;i<=n;i++)
{
if (dfn[i]==0)tarjan(i);
}
for (register int i=1;i<=n;i++)
{
cout<<group[i]<<endl;
}
return 0;
}
(ただしluoguテンプレートまたは癌のほとんどありません)
古典的な例を参照してください。
例Luogu P2341 [HAOI2006]人気の牛| [テンプレート]強連結成分
タイトル説明
それぞれの牛はブルペンでのスターを夢見ます。すべての牛が牛であると同じように牛が星です。すべてのミルク
牛は常に所有したいと牛あたりの自己陶酔しています。こんにちは場合 - お届けすることができます「のような」牛の間
フアンB、C、またC.等、次いでAのようなB N牛舎の牛の合計は、あなたの牛の数との愛の関係を与えられました
スターになることができますどのように多くの牛を計算します。
入力形式
最初のライン:
NとMは整数で分離された2つの空間M + 1行に第二列:
2つのスペースで各ラインは、整数を分離:AとB、及びAは、Bのように表します出力フォーマット
最初の行:牛の星の数を表す単一の整数
データ範囲
(\ 10 \%\)データ\(N \の当量20、M \の当量50 \)
(\ 30 \%\)データ\(N \の当量1000と、M \の当量20000 \)
(\ 70 \%\)データ\(N \の当量5000、M \の当量50000 \)
\(100 \%\)データ\(N \の当量10000、M \の当量50000 \)
あなたは問題がこのああを理解していないかを確認することはできません
今、Bの接続有向エッジにAを指すものとする「Bによるへようこそ」を参照してください。
...その後、強連結成分は、限りの大きさが1ではないとして、すべてお互いのような牛があります。
限り、我々は再び縮小する点として、そして強くコンポーネント0に接続する度合いを知る、すべての牛は牛でこの強連結成分を好きになるでしょう。
したがって、正しいアプローチは、です。
- ポイント還元
- ゼロを指すように学位をご覧ください。
しかし、強連結成分のうち二つが0である場合ならば、それは何の星ではないではないことに注意してください。
図DPへのクラス
後遺症を治療しなければ、上記のDPを実行する際の一般的有向グラフため(DAGを保証されていない)、
我々は、典型的には最初の点によって減少した後、トポロジカルソートDPを実行することができます。
[テンプレート]実施例1 LuoguP3387の集光点
トピックの背景
凝縮ポイント+ DP
タイトル説明
ポイントを与え、nおよびmエッジがグラフに向け、各点は、重量、点の加重和と最大値を通る経路を求める経路を有しています。あなただけのこの要件と重量のうち。
複数のエッジまたはポイントを通過できるように、しかし、繰り返しポイントの後、体重は一度だけ計算されます。
入力形式
最初の行、N、M
第二行、N-整数、代表点右折
二つの整数、U、Vの第三のラインにM + 2、U-を表す> V有向エッジが存在します
出力フォーマット
総行、最大点の右側。
質問は、私たちがdpの種類「すべてのために」を起動し、シュリンクtarjanポイントながら、その後、toposortから出てくる、行うことができます。
コードの実装:
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
template <typename Tp>
inline Tp Read()
{
Tp num = 0;
char ch = getchar();
bool flag = false;
while (!isdigit(ch))
flag |= ch == '-', ch = getchar();
while (isdigit(ch))
num = (num << 1) + (num << 3) + (ch ^ 48), ch = getchar();
return flag ? -num : num;
}
struct gragh
{
struct edge
{
int from, to, next;
};
edge Edge[100086];
int cnt, head[10086];
inline void add_edge(int from, int to)
{
this->cnt++;
this->Edge[this->cnt] = (edge){from, to, this->head[from]};
this->head[from] = this->cnt;
}
} a, b;
int n, m, val[10086];
// tarjan uses
int dfn[10086], low[10086], stack[10086], dfncnt, top;
bool instack[10086];
int group[10086], gcnt, gval[10086], In[10086];
// 缩点 解决dp后效性问题
void tarjan(int n)
{
dfn[n] = low[n] = ++dfncnt;
stack[++top] = n;
instack[n] = true;
for (int i = a.head[n]; i; i = a.Edge[i].next)
{
int to = a.Edge[i].to;
if (dfn[to] == 0)
{
tarjan(to);
low[n] = min(low[n], low[to]);
}
else
{
if (instack[to])
{
low[n] = min(low[n], low[to]);
}
}
}
if (dfn[n] == low[n])
{
int now;
gcnt++;
// gval[gcnt]=val[n];
do
{
now = stack[top--];
instack[now] = false;
group[now] = gcnt;
gval[gcnt] += val[now];
} while (now != n);
}
}
// rebuild the gragh
void rebuild()
{
for (int i = 1; i <= a.cnt; i++)
{
int from = a.Edge[i].from, to = a.Edge[i].to;
if (group[from] != group[to])
{
b.add_edge(group[from], group[to]);
In[group[to]]++;
}
}
}
int dis[10086], ans = 0;
// dp on the DAG
void toposort()
{
queue<int> q;
for (int i = 1; i <= gcnt; i++)
{
if (In[i] == 0)
q.push(i);
dis[i] = gval[i];
}
while (!q.empty())
{
int from = q.front();
q.pop();
for (int i = b.head[from]; i; i = b.Edge[i].next)
{
int to = b.Edge[i].to;
dis[to] = max(dis[from] + gval[to], dis[to]);
In[to]--;
if (In[to] == 0)
q.push(to);
}
}
for (int i = 1; i <= gcnt; i++)
{
ans = max(ans, dis[i]);
}
}
int main()
{
n = Read<int>(), m = Read<int>();
for (int i = 1; i <= n; i++)
val[i] = Read<int>();
for (int i = 1; i <= m; i++)
{
a.add_edge(Read<int>(), Read<int>());
}
for (int i = 1; i <= n; i++)
if (dfn[i] == 0)
{
tarjan(i);
}
rebuild();
toposort();
printf("%d\n", ans);
return 0;
}
実施例2 LuoguP3916図トラバーサル
タイトル説明
所与\(N \)点、\(M \)各ポイントのための有向グラフのエッジ、\(V \) 、検索\((V)\) 、\((V)\)点から表現\(V \)出発点は、の最大数に到達します。
入力形式
最初の行、2つの整数\(N、M \) 。
次\(M \)線は、それぞれ2の整数\(U_i、V_I \) 、エッジ表す\((U_i、V_I)を\) 。点\(1、2、\ cdots 、N \) 数。
出力フォーマット
\(\ N-)整数\((1)、A(2)、\ cdots、A(N)\) 。
データ範囲
以下のための\(60 \%の\)データ、(1 \のLeq N、K \のLeq 10 ^ 3 \)\
のための\(100 \%\)データ、\(1 \のLeq N、M、\のLeq 10 ^ 5 \)
注目すべきは、DFSまたはBFSは、問題を取り除くことができます。
しかし、我々はまた、道の最初の収縮スリーブDPポイントトポ並べ替えエレガント交流をすることができます。
コードの実装:
#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
struct edge
{
int from, to, next;
};
struct gragh
{
edge Edge[100086];
int cnt, head[100086];
inline void add_edge(int from, int to)
{
this->cnt++;
this->Edge[this->cnt].from = from;
this->Edge[this->cnt].to = to;
this->Edge[this->cnt].next = this->head[from];
this->head[from] = cnt;
}
};
gragh a, b;
int dfn[100086], low[100086], dfncnt, stack[100086], top;
int group[100086], gcnt, maxid[100086];
bool vst[100086];
int n, m, In[100086], dp[100086];
void tarjan(int n)
{
dfn[n] = low[n] = ++dfncnt;
stack[++top] = n;
vst[n] = true;
for (int i = a.head[n]; i; i = a.Edge[i].next)
{
int to = a.Edge[i].to;
if (dfn[to] == 0)
{
tarjan(to);
low[n] = min(low[n], low[to]);
}
else if (vst[to])
{
low[n] = min(low[n], dfn[to]);
}
}
if (low[n] == dfn[n])
{
int now;
gcnt++;
do
{
now = stack[top--];
group[now] = gcnt;
maxid[gcnt] = max(maxid[gcnt], now);
vst[now] = false;
} while (now != n);
}
}
void rebuild()
{
for (int i = 1; i <= a.cnt; i++)
{
int from = a.Edge[i].from;
int to = a.Edge[i].to;
if (group[from] != group[to])
{
In[group[to]]++;
b.add_edge(group[from], group[to]);
}
}
}
void toposort()
{
queue<int> q;
for (int i = 1; i <= gcnt; i++)
if (In[i] == 0)
q.push(i);
while (!q.empty())
{
int from = q.front();
q.pop();
dp[from] = max(dp[from], maxid[from]);
for (int i = b.head[from]; i; i = b.Edge[i].next)
{
int to = b.Edge[i].to;
dp[to] = max(dp[from], dp[to]);
if (--In[to] == 0)
q.push(to);
}
}
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= m; i++)
{
int f, t;
scanf("%d%d", &f, &t);
a.add_edge(t, f);
}
for (int i = 1; i <= n; i++)
{
if (dfn[i] == 0)
tarjan(i);
}
rebuild();
toposort();
for (int i = 1; i <= n; i++)
{
printf("%d ", dp[group[i]]);
}
return 0;
}
残りの最初つぶやい