最大半联通子图
题目描述
一个有向图G=(V,E)称为半连通的(Semi-Connected),如果满足:?u,v∈V,满足u→v或v→u,即对于图中任意两点u,v,存在一条u到v的有向路径或者从v到u的有向路径。若G'=(V',E')满足V'?V,E'是E中所有跟V'有关的边,则称G'是G的一个导出子图。若G'是G的导出子图,且G'半连通,则称G'为G的半连通子图。若G'是G所有半连通子图中包含节点数最多的,则称G'是G的最大半连通子图。给定一个有向图G,请求出G的最大半连通子图拥有的节点数K,以及不同的最大半连通子图的数目C。由于C可能比较大,仅要求输出C对X的余数。
输入输出格式
输入格式:
第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。
输出格式:
应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.
输入输出样例
输入样例#1:
6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4
输出样例#1:
3
3
说明
对于100%的数据, (N≤100000,M≤1000000,X≤10^8)
题解
bzoj1093
我们把题目所给的图画出来
题目要求求出一条路径,这条路径经过的点要最多,并输出这种路径的条数
可以发现,1,2两个点是可以互相到达的,那么就可以缩点,记录一下强连通分量内的点的个数,方便以后的操作
做到这里其实当时我是不知道接下来怎么做的,所以就打了个搜索
但是把缩点之后拿出来一看,突然就有了思路
因为原图是一个有向图,缩点之后就变成DAG,那么我们就可以topsort一下,确定我们记录的顺序,再然后发现可以dp
那么自然而然的最值就出来了,怎么统计方案数?
做过最短路计数的应该比较容易理解
在topsort中,对与一个从队列中取出的点u,和它可以到达的其中一个点v
如果被u更新的答案比原来v的答案更优,那么v的方案数就由u延续下来
也就是
if(dp[v]<dp[u]+num[v])//其中num[i]为i这个强连通分量内点的个数
dp[v]=dp[u]+num[v],cnt[v]=cnt[u]
如果被u更新的答案与v之前的答案一样,那么说明又多了一些方案可以到达这个答案
if(dp[v]==dp[u]+num[v])
cnt[v]+=cnt[u];
那么整个思路应该就出来了
- 对原图缩点
- 对新的DAG重新建边,注意一点,缩点直接建边会有很多重复的边,所以要去重
- 统计入度为0的点,并加入队列进行topsort并dp
- 统计答案
Code
#include<bits/stdc++.h>
#define Min(a,b) (a)<(b)?(a):(b)
#define Max(a,b) (a)>(b)?(a):(b)
using namespace std;
typedef long long lol;
const lol N=100010,M=1000010;
void in(lol &ans)
{
ans=0;lol f=1;char i=getchar();
while(i<'0' || i>'9') {if(i=='-') f=-1;i=getchar();}
while(i>='0' && i<='9') ans=(ans<<1)+(ans<<3)+(i^48),i=getchar();
ans*=f;
}
stack<lol>s;
lol n,m,cnt,mod,dfscnt,scc,t;
lol ans,sum;
lol to[M<<1],nex[M<<1],head[N];
lol low[N],dfn[N],sccno[N],num[N],dp[N],dg[N],tq[N];
struct E{
lol x,y;
}e[M],A[M];
inline void add(lol a,lol b) {
to[++cnt]=b;
nex[cnt]=head[a];
head[a]=cnt;
}
bool cmp(E a,E b) {
if(a.x==b.x) return a.y<b.y;
return a.x<b.x;
}
void tarjan(lol u)
{
dfn[u]= low[u] = ++dfscnt; s.push(u);
for(lol i=head[u];i;i=nex[i])
if(!dfn[to[i]]) tarjan(to[i]),low[u]=Min(low[u],low[to[i]]);
else if(!sccno[to[i]]) low[u]=Min(low[u],dfn[to[i]]);
if(low[u]==dfn[u]) {
scc++;
while(1) {
lol x=s.top(); s.pop(); num[scc]++; sccno[x]=scc;
if(x==u) break;
}
}
}
void topsort_dp()
{
queue<lol>q;
for(lol i=1;i<=scc;i++) if(!dg[i]) q.push(i),dp[i]=num[i],tq[i]=1;
while(!q.empty()) {
lol u=q.front(); q.pop();
for(lol i=head[u];i;i=nex[i]) {
if(dp[to[i]]<dp[u]+num[to[i]]) dp[to[i]]=dp[u]+num[to[i]],tq[to[i]]=tq[u];
else if(dp[to[i]]==dp[u]+num[to[i]]) (tq[to[i]]+=tq[u])%=mod;
if(!(--dg[to[i]])) q.push(to[i]);
}
}
}
int main()
{
in(n), in(m) , in(mod);
for(lol i=1;i<=m;i++) {
lol a,b; in(a),in(b);
add(a,b); e[i]=(E) {a,b};
}
for(lol i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
cnt=0; memset(head,0,sizeof(head));
for(int i=1;i<=m;i++) {
int fx=sccno[e[i].x],fy=sccno[e[i].y];
if(fx!=fy) A[++t] = (E) {fx,fy};
}
sort(A+1,A+1+t,cmp);
for(lol i=1;i<=t;i++)
if(A[i].x!=A[i-1].x || A[i].y!=A[i-1].y)
add(A[i].x,A[i].y),dg[A[i].y]++;
topsort_dp();
for(int i=1;i<=scc;i++) {
if(dp[i]>ans) {ans=dp[i],sum=tq[i];}
else if(dp[i]==ans) (sum+=tq[i])%=mod;
}
printf("%lld
%lld
",ans,sum);
}