YBTOJ最大半连通子图&删点次数
全篇重点(奇怪的bug):
tarjan缩点+topo排序求最长链的常见问题:
(1)建新图的时候没有用缩点后生成的新点,而是用原来的点建图
(2)建新图的时候注意不要建重复的边,最好用并查集维护两个新点是否被连接过
✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧✧
具体题目:
一、最大半连通子图:
题目算法要素:tarjan缩点&拓扑dp&DAG的神奇性质
洛谷传送门
分析题目,可以发现,半连通子图要求其中的两个点之间存在一条可以从一个点到达另一个点的有向路径。而题目对半连通子图的定义方式很像强连通分量。
另外,显然强连通分量一定是半连通子图或半连通子图的一部分,因此直接缩点,显然没错。
然后考虑如何生成最大半连通子图,由于缩点之后的图是一张DAG。
经过思考就可以得出DAG的一个关键性质:如果一个点的集合中,每两个点都有一条路径从一个点到达另一个点,则这个点的集合一定是一条链。
至此,我们已经把问题转化成了:求缩点后DAG上的最长链。
由于缩点相当于是把一个强连通分量中的所有数据都合并到一个点中,那么本题中就相当于把缩点后DAG中每个点所代表的点数作为点权,求一条点权和最大的链
最长链,或者叫最大权链,都可以通过拓扑dp解决。
最后总结一下流程:tarjan缩点+建新图+拓扑dp
Code
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5,maxm=2e6+5;
int head[maxn],ecnt=-1,col[maxn],f[maxn],sum[maxn],ans[maxn],dp[maxn];
bool vis[maxn];
int s[maxn],top,tot,Time,dfn[maxn],low[maxn],n,m,a,b,mod,indu[maxn];
vector<int> e2[maxn];
struct mint
{
int nxt,u,v;
}e[maxm];
inline void addline(int u,int v)
{
e[++ecnt].nxt=head[u];
e[ecnt].u=u;
e[ecnt].v=v;
head[u]=ecnt;
}
void tarjan(int now)
{
dfn[now]=low[now]=++Time;
s[++top]=now;
vis[now]=true;
for(int i=head[now];~i;i=e[i].nxt)
{
int v=e[i].v;
if(!dfn[v])
{
tarjan(v);
low[now]=min(low[now],low[v]);
}
else if(vis[v]) low[now]=min(low[now],dfn[v]);
}
if(dfn[now]==low[now])
{
int cur;
tot++;
do
{
cur=s[top--];
sum[tot]++;
col[cur]=tot;
vis[cur]=false;
}while(cur!=now);
}
}
void topo()
{
queue<int> q;
for(int i=1;i<=tot;++i)
{
if(!indu[i])
{
q.push(i);
dp[i]=sum[i];
ans[i]=1;
}
}
while(!q.empty())
{
int u=q.front();
q.pop();
for(int y : e2[u])
{
if(dp[y]<dp[u]+sum[y])
{
dp[y]=dp[u]+sum[y];
ans[y]=ans[u];
}
else if(dp[y]==dp[u]+sum[y])
{
ans[y]=(ans[y]+ans[u])%mod;
}
--indu[y];
if(indu[y]==0) q.push(y);
}
}
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d%d",&n,&m,&mod);
for(int i=1;i<=m;++i)
{
scanf("%d%d",&a,&b);
addline(a,b);
}
for(int i=1;i<=n;++i)
{
if(!dfn[i]) tarjan(i);
}
for(int i=0;i<=m-1;++i)
{
int a=e[i].u,b=e[i].v;
if(col[a]!=col[b])
{
e2[col[a]].push_back(col[b]);
}
}
for(int i=1;i<=tot;++i)
{
sort(e2[i].begin(),e2[i].end());
e2[i].erase(unique(e2[i].begin(),e2[i].end()),e2[i].end());
for(int y : e2[i]) ++indu[y];
}
topo();
int maxx=0,totans=0;
for(int i=1;i<=tot;++i)
{
if(dp[i]>maxx)
{
maxx=dp[i];
totans=ans[i];
}
else if(dp[i]==maxx)
{
totans+=ans[i];
if(totans>=mod) totans-=mod;
}
}
printf("%d\n%d",maxx,totans);
return 0;
}