bzoj 1093 [ZJOI2007]最大半连通子图(scc+DP)

时间:2023-03-09 06:26:33
bzoj 1093 [ZJOI2007]最大半连通子图(scc+DP)

1093: [ZJOI2007]最大半连通子图

Time Limit: 30 Sec  Memory Limit: 162 MB
Submit: 2286  Solved: 897
[Submit][Status][Discuss]

Description

bzoj 1093 [ZJOI2007]最大半连通子图(scc+DP)

Input

第一行包含两个整数N,M,X。N,M分别表示图G的点数与边数,X的意义如上文所述。接下来M行,每行两个正整数a, b,表示一条有向边(a, b)。图中的每个点将编号为1,2,3…N,保证输入中同一个(a,b)不会出现两次。

Output

应包含两行,第一行包含一个整数K。第二行包含整数C Mod X.

Sample Input

6 6 20070603
1 2
2 1
1 3
2 4
5 6
6 4

Sample Output

3
3

HINT

对于100%的数据, N ≤100000, M ≤1000000;对于100%的数据, X ≤10^8。

Source

【思路】

强连通分量+拓扑排序+DP

求scc,锁点。则问题转化为求DAG上的最长路,在拓扑序上进行DP即可。 

    需要注意的是重新构图的时候会有重边,为了防止重复计数需要特别处理一下。

【代码】

 #include<cstdio>
#include<stack>
#include<queue>
#include<vector>
#include<cstring>
#include<iostream>
using namespace std; const int maxn = +; vector<int> g[maxn],G[maxn];
int pre[maxn],lowlink[maxn],sccno[maxn],scccnt,dfsclock;
stack<int> S; int dfs(int u) {
pre[u]=lowlink[u]=++dfsclock;
S.push(u);
for(int i=;i<g[u].size();i++) {
int v=g[u][i];
if(!pre[v]) {
dfs(v);
lowlink[u]=min(lowlink[u],lowlink[v]);
}
else if(!sccno[v]) {
lowlink[u]=min(lowlink[u],pre[v]);
}
}
if(lowlink[u]==pre[u]) {
++scccnt;
for(;;) {
int x=S.top() ; S.pop();
sccno[x]=scccnt;
if(x==u) break;
}
}
}
void findscc(int n) {
memset(pre,,sizeof(pre));
memset(sccno,,sizeof(sccno));
dfsclock=scccnt=;
for(int i=;i<n;i++)
if(!pre[i]) dfs(i);
} int n,m,MOD;
int d[maxn],cnt[maxn]; int val[maxn],in[maxn];
void build() {
for(int i=;i<n;i++) {
val[sccno[i]]++;
for(int j=;j<g[i].size();j++) {
int v=g[i][j];
if(sccno[i]==sccno[v]) continue;
in[sccno[v]]++;
G[sccno[i]].push_back(sccno[v]);
}
}
}
void solve() {
queue<int> q;
int vis[maxn];
for(int i=;i<=scccnt;i++) if(!in[i]) {
d[i]=val[i] , cnt[i]=;
q.push(i);
}
while(!q.empty()) {
int u=q.front(); q.pop();
for(int i=;i<G[u].size();i++) {
int v=G[u][i];
if(!(--in[v])) q.push(v);
if(vis[v]!=u) { //重新构图后有重边 防止重复计数
if(d[v]<d[u]+val[v]) {
d[v]=d[u]+val[v]; cnt[v]=cnt[u];
}
else if(d[v]==d[u]+val[v])
cnt[v]=(cnt[v]+cnt[u])%MOD;
}
vis[v]=u;
}
}
} int main() {
scanf("%d%d%d",&n,&m,&MOD);
int u,v;
while(m--) {
scanf("%d%d",&u,&v);
u-- , v--;
g[u].push_back(v);
}
findscc(n);
build();
solve();
int ans=,ansnum=;
for(int i=;i<=scccnt;i++)
if(ans<d[i]) ans=d[i] , ansnum=cnt[i];
else if(ans==d[i]) ansnum=(ansnum+cnt[i])%MOD;
printf("%d\n%d",ans,ansnum);
return ;
}