「图论」第4章 强连通分量课堂过关
「图论」第4章 强连通分量课堂过关
A. 【例题1】有向图缩点
题目
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
#define N 10010
#define M 100010
int read() {
int re = 0;
bool sig = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-')
sig = 1;
c = getchar();
}
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return sig ? -re : re;
}
struct node {
int to , nxt;
}ed[2][M];
int head[2][N];
void addedge(int id , int u , int v) {
static int cnt[2];
node *e = ed[id];
int *h = head[id];
int c = ++cnt[id];
e[c].to = v , e[c].nxt = h[u] , h[u] = c;
return;
}
int n , m;
int val[N];
int dfn[N] , low[N];
int stack[N] , top;
int col[N];
int sum[N] ;
int tot;
int maxv[N];
int ind[N];
void Tarjan(int x) {
static int Time = 0;
dfn[x] = low[x] = ++Time;
stack[++top] = x;
for(int i = head[0][x] ; i ; i = ed[0][i].nxt) {
int to = ed[0][i].to;
if(!dfn[to]) {
Tarjan(to);
if(low[to] < low[x])
low[x] = low[to];
}
else if(col[to] == 0 && low[x] > low[to])
low[x] = low[to];
}
if(dfn[x] == low[x]) {
col[x] = ++tot;
sum[tot] += val[x];
while(stack[top] != x)
sum[tot] += val[stack[top]] , col[stack[top]] = tot , --top;
top--;
}
}
int main() {
n = read() , m = read();
for(int i = 1 ; i <= n ; i++)
val[i] = read();
for(int i = 1 ; i <= m ; i++) {
int u = read() , v = read();
addedge(0 , u , v);
}
for(int i = 1 ; i <= n ; i++)
if(dfn[i] == 0)
Tarjan(i);
for(int i = 1 ; i <= n ; i++)
for(int j = head[0][i] ; j ; j = ed[0][j].nxt) {
int u = col[i] , v = col[ed[0][j].to];
if(u != v) {
addedge(1 , u , v);
++ind[v];
}
}
queue <int> q;
for(int i = 1 ; i <= tot ; i++)
if(ind[i] == 0){
maxv[i] = sum[i];
q.push(i);
}
while(!q.empty()) {
int u = q.front();
q.pop();
for(int i= head[1][u] ; i ; i = ed[1][i].nxt) {
int v = ed[1][i].to;
if(maxv[u] + sum[v] > sum[v])
sum[v] = maxv[u] + sum[v];
--ind[v];
if(ind[u] == 0)
q.push(v);
}
}
int ans = 0;
for(int i = 1 ; i <= tot ; i++)
if(ans < maxv[i])
ans = maxv[i];
cout << ans;
return 0;
}
B. 【例题2】受欢迎的牛
题目
思路
按喜欢关系建图,跑一边tarjan,若生成的DAG中同时存在多个出度为0的点,则这些点一定不能互相到达,答案为0,否则,出度为0的点代表的原图强连通块的点即为明星.
代码
#include <iostream>
#include <cstdio>
#define N 10010
#define M 100010
using namespace std;
int read() {
int re = 0;
bool sig = 0;
char c = getchar();
while(c < '0' || c > '9') {
if(c == '-')
sig = 1;
c = getchar();
}
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0' , c = getchar();
return sig ? -re : re;
}
struct node{
int to , nxt;
}ed[M];
int head[N];
void addedge(int u , int v) {
static int cnt = 0;
++cnt;
ed[cnt].nxt = head[u] , ed[cnt].to = v , head[u] = cnt;
}
int n , m;
int n2;
int dfn[N] , low[N];
int col[N] , w[N];
int stac[N] , top;
void tarjan(int x) {
static int Time = 0;
stac[++top] = x;
dfn[x] = low[x] = ++Time;
for(int i = head[x] ; i ; i = ed[i].nxt) {
int to = ed[i].to;
if(dfn[to] == 0) {
tarjan(to);
if(low[to] < low[x])
low[x] = low[to];
}
else if(col[to] == 0)
if(low[to] < low[x])
low[x] = low[to];
}
if(dfn[x] == low[x]) {
++n2;
col[x] = n2;
++w[n2];
while(stac[top] != x) {
col[stac[top]] = n2;
++w[n2];
--top;
}
--top;
}
}
int out[N];
int main() {
n = read();
m = read();
for(int i = 1 ; i <= m ; i++) {
int u = read() , v = read();
addedge(u , v);
}
for(int i = 1 ; i <= n ; i++)
if(dfn[i] == 0)
tarjan(i);
for(int i = 1 ; i <= n ; i++)
for(int j = head[i] ; j ; j = ed[j].nxt) {
int u = col[i] , v = col[ed[j].to];
if(u == v)continue;
++out[u];
}
int k = -1;
for(int i = 1 ; i <= n2 ; i++) {
if(out[i] == 0) {
if(k == -1) k = i;
else {
cout << 0;
return 0;
}
}
}
cout << w[k];
return 0;
}
D. 【例题4】恒星的亮度
题目
思路
首先,这题用到差分约束,没学的先学
鉴于恒星亮度为整数,我们转换一下题目所给的关系:
\[\begin{cases}
A=B\qquad\qquad &A\ge B \and B\ge A\\
A < B &B \ge A+1\\
A \ge B & A \ge B\\
A > B & A \ge B+1\\
A \le B & B \ge A
\end{cases}
\]
这样,原关系就只剩下\(\ge\)了.根据差分约束思想,若\(A\ge B+val\)就从\(B\)到\(A\)连一条权值为\(val\),从每个入度为0的点出发跑一边最长路,距离即亮度, 若出现正环,则说明有\(A\ge B ,B\ge C , C > A\)的错误关系,输出-1
.
问题是,怎么判断正环呢?SPFA?必TLE!
看到这题边权只有0,1两种情况,不难想到,当一个强连通块内存在一条正权边时,必有正环,输出-1
剩下的,交给拓扑DP即可
代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <cstring>
using namespace std;
int read() {
int re = 0;
char c = getchar();
while(c < '0' || c > '9') c = getchar();
while(c >= '0' && c <= '9')
re = (re << 1) + (re << 3) + c - '0',
c = getchar();
return re;
}
#define N 100010
#define M 200010
struct node {
int to , nxt , val;
}ed[M * 2];
int HEAD[2][N];
int *head;
void addedge(int u , int v , int val) {
static int cnt = 0;
++cnt;
ed[cnt].nxt = head[u] , ed[cnt].to = v , ed[cnt].val = val , head[u] = cnt;
}
int n , m , n2;
int dfn[N] , low[N] , col[N] , w[N];
int stac[N] , top;
int dist[N];
void tarjan(int x) {
static int Time = 0;
dfn[x] = low[x] = ++Time;
stac[++top] = x;
for(int j = 1 ; j >= 0 ; j--)
for(int i = head[x] ; i ; i = ed[i].nxt) {
if(ed[j].val != j) continue;
int to = ed[i].to;
if(dfn[to] == 0) {
tarjan(to );
if(low[to] < low[x])
low[x] = low[to];
}
else if(col[to] == 0) {
if(low[to] < low[x])
low[x] = low[to];
}
}
if(dfn[x] == low[x]) {
col[x] = ++n2;
while(x != stac[top]) {
col[stac[top]] = n2;
--top;
}
--top;
}
}
int ind[N];
int main() {
n = read() , m = read();
head = HEAD[0];
for(int i = 1 ; i <= m ; i++) {
int T = read();
int u = read() , v = read();
switch(T) {
case 1 :
addedge(u , v , 0);
addedge(v , u , 0);
break;
case 2 :
addedge(u , v , 1);
break;
case 3 :
addedge(v , u , 0);
break;
case 4 :
addedge(v , u , 1);
break;
case 5 :
addedge(u , v , 0);
break;
}
}/*
for(int i = 1 ; i <= n ; i++) {
cout << i << ":\t";
for(int j = head[i] ; j ; j = ed[j].nxt)
cout << ed[j].to << ',' << ed[j].val << ' ';
cout << endl;
}*/
for(int i = 1 ; i <= n ; i++)
if(dfn[i] == 0)
tarjan(i);
head = HEAD[1];
for(int i = 1 ; i <= n ; i++)
for(int j = HEAD[0][i] ; j ; j = ed[j].nxt) {
int u = col[i] , v = col[ed[j].to];
if(u == v) {
if(ed[j].val > 0) {
cout << -1;
return 0;
}
continue;
}
++ind[v];
addedge(u , v , ed[j].val);
}
queue <int> q;
memset(dist , 0 , sizeof(dist));
for(int i = 1 ; i <= n2 ; i++)
if(ind[i] == 0) {
dist[i] = 1;
q.push(i);
}
while(!q.empty()) {
int k = q.front();
q.pop();
for(int i = head[k] ; i ; i = ed[i].nxt) {
int to = ed[i].to;
if(--ind[to] == 0)
q.push(to);
if(dist[k] + ed[i].val > dist[to])
dist[to] = dist[k] + ed[i].val;
}
}
long long ans = 0;
for(int i = 1 ; i <= n ; i++)
ans += dist[col[i]];
cout << ans;
return 0;
}