P2272 [ZJOI2007]最大半连通子图

P2272 [ZJOI2007]最大半连通子图

Tarjan缩点+拓扑排序

attention:注意判断新图中的重边

先用Tarjan缩点,处理出每个分量的大小。

然后建个新图,设叶子节点的方案数为1,蓝后跑一遍拓扑排序即可。

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<cctype>
using namespace std;
template <typename T> inline T min(T &a,T &b) {return a<b ?a:b;}
template <typename T> inline T max(T &a,T &b) {return a>b ?a:b;}
template <typename T> inline void read(T &x){
    char c=getchar(); x=0; bool f=1;
    while(!isdigit(c)) f= !f||c=='-' ? 0:1,c=getchar();
    while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    x= f ? x:-x;
}
template <typename T> inline void output(T x){
    if(!x) {putchar(48); return ;}
    if(x<0) putchar('-'),x=-x;
    int wt[50],l=0;
    while(x) wt[++l]=x%10,x/=10;
    while(l) putchar(wt[l--]+48);
}
struct data{
    int f,t;
    bool operator < (const data &tmp) const{return f<tmp.f||(f==tmp.f&&t<tmp.t);}
    bool operator != (const data &tmp) const{return f!=tmp.f||t!=tmp.t;}
}a[1000002];
int n,m,mod,d[100002],ted; long long f[100002],ans;
int dfs_clock,dfn[100002],low[100002],_top,st[100002],tot,be[100002],val[100002];
int cnt1,hd1[100002],nxt1[1000002],ed1[100002],poi1[1000002];
int cnt2,hd2[100002],nxt2[1000002],ed2[100002],poi2[1000002],_in[100002];
int rts,rt[100002],mxd;
queue <int> h;
inline void add1(int x,int y){
    nxt1[ed1[x]]=++cnt1; hd1[x]= hd1[x] ? hd1[x]:cnt1;
    ed1[x]=cnt1; poi1[cnt1]=y;
}
inline void add2(int x,int y){
    nxt2[ed2[x]]=++cnt2; hd2[x]= hd2[x] ? hd2[x]:cnt2;
    ed2[x]=cnt2; poi2[cnt2]=y; ++_in[y];
}
inline void tarjan(int x){
    dfn[x]=low[x]=++dfs_clock; st[++_top]=x;
    for(int i=hd1[x];i;i=nxt1[i]){
        if(!dfn[poi1[i]]) tarjan(poi1[i]),low[x]=min(low[x],low[poi1[i]]);
        else if(!be[poi1[i]]) low[x]=min(low[x],dfn[poi1[i]]);
    }
    if(low[x]==dfn[x]){
        be[x]=++tot; ++val[tot];
        while(st[_top]!=x) be[st[_top--]]=tot,++val[tot];
        --_top;
    }
}
void find(){ //拓扑排序
    for(int i=1;i<=tot;++i)
        if(!_in[i])
            h.push(i),d[i]=val[i],f[i]=1;
    while(!h.empty()){
        int x=h.front(); h.pop();
        if(!hd2[x]) {rt[++rts]=x; mxd=max(mxd,d[x]); continue;}
        for(int i=hd2[x];i;i=nxt2[i]){
            if(d[poi2[i]]<d[x]+val[poi2[i]]){ //如果深度更大就重新结算
                d[poi2[i]]=d[x]+val[poi2[i]];
                f[poi2[i]]=f[x]%mod;
            }else if(d[poi2[i]]==d[x]+val[poi2[i]]){
                f[poi2[i]]=(f[poi2[i]]+f[x])%mod;
            }
            if((--_in[poi2[i]])==0) h.push(poi2[i]);
        }
    }
}
int main(){
    read(n); read(m); read(mod); int q1,q2;
    for(int i=1;i<=m;++i) read(q1),read(q2),add1(q1,q2);
    for(int i=1;i<=n;++i) if(!dfn[i]) tarjan(i);
    for(int i=1;i<=n;++i)
        for(int j=hd1[i];j;j=nxt1[j])
            if(be[i]!=be[poi1[j]])
                a[++ted]=(data){be[i],be[poi1[j]]};
    sort(a+1,a+ted+1);
    for(int i=1;i<=ted;++i) //把重边判掉
        if(a[i]!=a[i-1])
            add2(a[i].f,a[i].t);
    find();
    for(int i=1;i<=rts;++i) if(d[rt[i]]==mxd) ans=(ans+f[rt[i]])%mod; //深度最大的累加起来
    output(mxd); putchar('
'); output(ans);
    return 0; 
}
原文地址:https://www.cnblogs.com/kafuuchino/p/9708916.html