今天是放假的第一天(不说什么废话了)
什么是半连通子图?就是此图中包含的所有点两两点之间至少有一条单向路径。
题目问了两个问题
1.最大半连通子图的大小
2.最大半连通子图的个数
好了,这个问题看上去确实恶心,但不难发现,一个强连通子图一定是半连通的。
而且任何点和强连通子图中的任意一个点有连接,那么它就和所有强连通子图中的点有半连通关系。
那么这真是极好的,tarjan缩点,一切都ok了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<vector>
#include<algorithm>
#include<stack>
using namespace std;
typedef long long ll;
inline ll read()
{
char ls=getchar(),k=0;for(;ls<'0'||ls>'9';k=ls,ls=getchar());
ll x=0;for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0';
if(k=='-')x=-x;return x;
}
stack<ll>zhan;
ll edge[1000005];
ll z[1000005];
ll last[100005];
ll low[100005];
ll dfn[100005];
ll pzhan[100005];
ll visit[100005];
ll mod;
ll n,m;
ll s;
////////////////////////
ll n2;
ll ji;
ll goal[100005];
ll dj[100005];
ll zhi[100005];
ll back[100005];
///////////////////////
ll ji2;
ll edge2[1000005];
ll z2[1000005];
ll last2[100005];
ll dq[100005];
ll rd[100005];
ll kp[100005];
ll sign;
////////////////////////
ll edge3[1000005];
ll z3[1000005];
ll last3[100005];
/////////////////////
ll maxn;
ll sum;
ll zl[100005];
ll maxx;
ll summ;
ll pd[100005];
inline void tarjan(ll d)//强连通分量
{
dfn[d]=low[d]=++s;
zhan.push(d);pzhan[d]=1;visit[d]=1;
for(ll k=last[d];k!=0;k=z[k])
{
ll v=edge[k];
if(visit[v]==0)
{
tarjan(v);
low[d]=min(low[d],low[v]);
}
else if(pzhan[v]==1)
{
low[d]=min(low[d],dfn[v]);
}
}
if(dfn[d]==low[d])//规划旧点集和新点集
{
++n2;
ll g=zhan.top();
while(dfn[g]!=low[g])
{
++dq[n2];
goal[g]=n2;
dj[++ji]=g;
zhi[ji]=back[n2];
back[n2]=ji;
pzhan[g]=0;
zhan.pop();
g=zhan.top();
}
++dq[n2];
goal[g]=n2;
dj[++ji]=g;
zhi[ji]=back[n2];
back[n2]=ji;
pzhan[g]=0;zhan.pop();
}
return;
}
void kpsort()//拓扑排序
{
for(int i=1;i<=n2;++i)
{
ll d=kp[i];
for(int k=last3[d];k!=0;k=z3[k])
{
ll v=edge3[k];
--rd[v];
if(rd[v]==0)
kp[++sign]=v;
}
}
return;
}
void dp()//拓扑图动态规划
{
for(int i=1;i<=n2;++i)
{
ll d=kp[i];
maxx=0;
summ=1;
for(int k=last2[d];k!=0;k=z2[k])
{
ll v=edge2[k];
if(dq[v]==maxx)
{
summ+=zl[v];
summ=summ%mod;
}
else if(dq[v]>maxx)
{
maxx=dq[v];
summ=zl[v];
}
}
dq[d]+=maxx;
zl[d]=summ;
if(dq[d]==maxn)
{
sum+=zl[d];
sum=sum%mod;
}
else if(dq[d]>maxn)
{
maxn=dq[d];
sum=zl[d];
}
}
return;
}
int main()
{
n=read(),m=read(),mod=read();
for(int i=1;i<=m;++i)
{
ll a1=read(),b1=read();
edge[i]=b1;
z[i]=last[a1];
last[a1]=i;
}
for(int i=1;i<=n;++i)
{
if(visit[i]==0)
tarjan(i);
}
for(int i=1;i<=n2;++i)//建立新图的边
{
for(int k=back[i];k!=0;k=zhi[k])
{
ll v=dj[k];
for(int kk=last[v];kk!=0;kk=z[kk])
{
ll u=edge[kk];
ll gs=goal[u];
if(gs!=i&&pd[gs]!=i)
{
pd[gs]=i;
++rd[gs];
edge2[++ji2]=i;//统计一个点的出边(拓扑排序用)
z2[ji2]=last2[gs];
last2[gs]=ji2;
edge3[ji2]=gs;//统计一个点的入边(动态规划用)
z3[ji2]=last3[i];
last3[i]=ji2;
}
}
}
}
for(int i=1;i<=n2;++i)//将入度为0的点进入拓扑序列
{
if(rd[i]==0)
{
zl[i]=1;
kp[++sign]=i;
}
}
kpsort();//拓扑排序
dp();//动态规划
cout<<maxn<<endl<<sum<<endl;
return 0;
}