首页 > 技术文章 > [WOJ2549]逻辑的连通性

kma093 2019-01-23 13:39 原文

题目描述:##

数学中,假如有命题 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;
}

推荐阅读