题目描述:##
数学中,假如有命题 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;
}