[WOJ2549]逻辑的连通性

题目描述:

数学中,假如有命题 p 一定能推出命题 q,则称 p 是 q 的充分条件,q 是 p 的必要 条件。

特别的,当 p 既是 q 的充分条件,又是 q 的必要条件时,称 p 和 q 互为 充要条件

现在有 n 个命题,其中一些是另一些的充分条件。请问有多少对命题互为 充要条件?

输入

第一行两个正整数 n,m分别表示命题数和已知关系数

接下来 m 行,每行两个正整数 p 和 q,表示命题 p 是命题 q 的充分条件

输出

仅一行,一个整数,表示充要条件的对数

考试T2,tarjan手癌了……(u和v什么的果然容易打错)然后没开longlong,GG了

题目分析:

图论,tarjan
每给一组p q则从p往q连一条有向边
tarjan缩点之后每个连通分量里面的任意两个点都互为充要条件,所以tarjan里每找到一个连通分量的时候记一下这个连通分量的大小(弹栈的时候id[cnt]++),最后对于每个连通分量的大小\(k\)求一个\(C^2_k\)相加即可

代码:

#include<bits/stdc++.h>
#define M (600000+5)
#define N (50000+5)
using namespace std;
inline int read(){
    int cnt=0,f=1;char c;
    c=getchar();
    while(!isdigit(c)){
        if(c=='-')f=-f;
        c=getchar();
    }
    while(isdigit(c)){
        cnt=cnt*10+c-'0';
        c=getchar();
    }
    return cnt*f;
}
long long n,m,ans=0;
long long nxt[M],first[N],to[M],low[N],dfn[N],sign=0,sta[N],top=0,tot;
long long cnt,id[N];
bool insta[N];
void add(int x,int y){
    nxt[++tot]=first[x];
    first[x]=tot;
    to[tot]=y;
}
void Dfs(int u){
    low[u]=dfn[u]=++sign;
    sta[++top]=u;insta[u]=1;
    for(register int i=first[u];i;i=nxt[i]){
        int v=to[i];
        if(!dfn[v]){
            Dfs(v);low[u]=min(low[v],low[u]);
        }
        else if(insta[v]&&dfn[v]<low[u])low[u]=dfn[v];
    }   
    if(low[u]==dfn[u]) {
        cnt++;
        do{
            id[cnt]++;insta[sta[top]]=0;
        }while(sta[top--]!=u);
    }
}
long long ask(long long x) { return ((x-1)*x)/2; }
int main(){
//  freopen("logic.in","r",stdin);
//  freopen("logic.out","w",stdout);
    n=read();m=read();
    for(register int i=1;i<=m;i++){
        int x=read();int y=read();
        add(x,y);
    }
    for(register int i=1;i<=n;i++){
        if(!dfn[i])Dfs(i); 
    }

    for(register int i=1;i<=cnt;i++)
        ans+=ask(id[i]);
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/kma093/p/10308478.html