分析
求最大半连通子图<==>求最长无分叉链
使用两个表头分别存储原图h[]和缩点后的新图hs[]
C++ 代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5+10,M = 2e6+10;
int h[N],hs[N],e[M],ne[M],idx; //h[]为原图的表头,hs[]为缩点后新的表头
int n,m,mod;
int dfn[N],low[N],timestamp;
int stk[N],top;
int id[N],scc_cnt,siz[N];
bool in_stk[N];
LL f[N],g[N];
void add(int h[],int a,int b)
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void tarjan(int u) /tarjan求scc
{
dfn[u]=low[u]=++timestamp;
stk[++top]=u,in_stk[u]=true;
for(int i=h[u];~i;i=ne[i])
{
int j=e[i];
if(!dfn[j])
{
tarjan(j);
low[u]=min(low[u],low[j]);
}
else if(in_stk[j])
low[u]=min(low[u],dfn[j]);
}
if(dfn[u]==low[u])
{
++scc_cnt;
int y;
do{
y=stk[top--];
in_stk[y]=false;
id[y]=scc_cnt;
siz[scc_cnt]++; //统计每个scc的节点数
}while(y!=u);
}
}
int main()
{
memset(h ,-1 ,sizeof h);
memset(hs ,-1 ,sizeof hs);
scanf("%d%d%d",&n,&m,&mod);
int a,b;
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
add(h,a,b);
}
for(int i=1;i<=n;i++)
{
if(!dfn[i])
tarjan(i);
}
unordered_set<LL> s; //哈希表判断重边
for(int i=1;i<=n;i++)
{
for(int j=h[i];~j;j=ne[j])
{
int k=e[j];
int a=id[i],b=id[k];
LL has = a* 1000000ll + b; //计算每个边的哈希值
if(a!=b && !s.count(has)) //如果此边没有加过
{
add(hs,a,b); //建立一个缩完点后的新图
s.insert(has);
}
}
}
for(int i=scc_cnt;i>0;i--) //tarjan求完后,scc_cnt的编号递减顺序就是拓扑序
{
if(!f[i]) //如果当前点没有更新,就进行初始化
{
f[i]=siz[i]; //当前大小为该scc的节点数
g[i]=1;
}
for(int j=hs[i];~j;j=ne[j]) //对新图进行遍历
{
int k=e[j];
if(f[k]<f[i]+siz[k])
{
f[k]=f[i]+siz[k];
g[k]=g[i];
}
else if(f[k]==f[i]+siz[k])
{
g[k]=(g[k]+g[i])%mod;
}
}
}
LL maxf=0,sum=0;
for(int i=1;i<=scc_cnt;i++)
{
if(f[i]>maxf)
{
maxf=f[i];
sum=g[i];
}
else if(f[i]==maxf)
{
sum=(sum+g[i])%mod;
}
}
printf("%d\n",maxf);
printf("%d\n",sum);
return 0;
}
我想在建新图(缩完点的图)之前是应该把idx=0归零的吧(新图的边序号应该从新从1开始),但是中间加了这句就错了,是哪里理解错了吗