luogu P2272 [ZJOI2007] 最大半连通子图
题意
最大半连通图:对于图中任意两点 \(u\) 和 \(v\) 存在一条 \(u\) 到 \(v\) 的有向路径 或者 从 \(v\) 到 \(u\) 的有向路径,都有也行。
求一个图中不同的最大半连通子图的大小与数目。
思路
主思路:Tarjan,缩点,dp + 计数。
如果两点在一个 scc 内,则它们必然是半连通子图,为了方便计算,考虑 Tarjan 求强连通分量,缩点并重建一个 DAG。
在这个 DAG 中,半连通子图都是一条链,否则若有分支,必有至少两点无法抵达互相。所以 DAG 中最长链的长度,即为最大半连通子图的大小。而不同的最大半连通子图的数目就是最长链个数。
在求最长链的时候,考虑 dp 递推做法,同时需要在发现链长同为当前最优时另外计数。
代码:
注意:
- dp 倒序递推;
- 原图 scc 之间可能有重边,注意判重;
- 旧图新图之间注意区分变量名。
#include<bits/stdc++.h>
#define RG register
#define IL inline
using namespace std;
const int maxn = 100005;
const int maxm = 1000006;
IL int Read(); IL void Rite(int x);
struct node{
int fm,to,nxt;
}edge[maxm];
struct Node{
int fm,to,nxt;
}Edge[maxm];
int n,m,x,mo;
int head[maxm],cnt;
int dfn[maxn],low[maxn],ins[maxn];
int ti,scc;
stack<int> stk;
int nme[maxn],siz[maxn];
int Head[maxm],Cnt;
int dp[maxn],num[maxn];
int vis[maxn],ans,tmp;
IL void input(int u,int v){
edge[cnt] = {u,v,head[u]};
head[u] = cnt++;
}
void Input(int u,int v){
Edge[Cnt] = {u,v,Head[u]};
Head[u] = Cnt++;
}
IL void Tarjan(int u){
low[u] = dfn[u] = ++ti;
stk.push(u); ins[u] = 1;
for(RG int i = head[u];~i;i = edge[i].nxt){
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]){
scc++;
do{
x = stk.top();
stk.pop();
nme[x] = scc;
ins[x] = 0;
siz[scc]++;
}while(u != x);
}
}
int main(){
memset(head,-1,sizeof(head));
memset(Head,-1,sizeof(Head));
n = Read(); m = Read(); mo = Read();
for(RG int i = 1;i <= m;++i){
int a = Read(),b = Read();
input(a,b);
}
for(RG int i = 1;i <= n;++i)
if(!dfn[i]) Tarjan(i);
for(RG int u = 1;u <= n;++u){
for(RG int i = head[u];~i;i = edge[i].nxt){
int v = edge[i].to;
if(nme[u] != nme[v])
Input(nme[u],nme[v]);
}
}
for(RG int i = 1;i <= scc;++i)
num[i] = 1,dp[i] = siz[i];
for(RG int u = scc;u >= 1;--u){
for(RG int i = Head[u];~i;i = Edge[i].nxt){
int v = Edge[i].to;
if(vis[v] == u) continue;
//注意判重,不能用 bool
vis[v] = u;
if(dp[v] < dp[u] + siz[v]){
dp[v] = dp[u] + siz[v];
num[v] = num[u];
}
else if(dp[v] == dp[u] + siz[v])
num[v] = (num[u] + num[v]) % mo;
}
}
for(RG int i = 1;i <= scc;++i){//注意倒序
if(dp[i] > ans)//求解最优
ans = dp[i],tmp = num[i];
else if(dp[i] == ans)//统计数目
tmp = (tmp + num[i]) % mo;
}
Rite(ans);
putchar('\n');
Rite(tmp);
return 0;
}
IL int Read(){
char c = getchar();
int x = 0,f = 1;
while(c < '0' || c > '9'){
if(c == '-') f = -1;
c = getchar();
}
while(c >= '0' && c <= '9'){
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
IL void Rite(int x){
if(x < 0) putchar('-'),x = -x;
if(x > 9) Rite(x / 10);
putchar(x % 10 + '0');
}
真毒瘤,妈的。

浙公网安备 33010602011771号