Time Limits: 2000 ms Memory Limits: 262144 KB Detailed Limits
Goto ProblemSet
Description
假如有命题p 一定能推出命题q,则称p 是q 的充分条件,q 是p 的必要条件。
特别的,当p 既是q 的充分条件,又是q 的必要条件时,称p 和q 互为充要条件
现在有n 个命题,其中一些是另一些的充分条件。请问有多少对命题互为充要条件?
Input
第一行三个正整数n,m,分别表示命题数、已知关系数
接下来m 行,每行两个正整数p 和q,表示命题p 是命题q 的充分条件
Output
仅一行,一个整数,表示充要条件的对数
Sample Input
5 5
1 3
3 2
2 1
4 5
5 4
Sample Output
4
样例说明:
4 对充要条件分别是(1, 2)、(2, 3)、(1, 3)、(4, 5)
Data Constraint
对于10% 的数据,n <= 10;m <= 50
对于40% 的数据,n <= 500;m <= 1000
对于另外10% 的数据,数据中保证没有重边且m = n^2
对于100% 的数据,n<= 50000;m <= 600000
dfn[u]表示在深度搜索中遍历到该节点的次序
low[u]表示以u节点为树根,u及u以下树节点所能找到的最小次序号
uses math;
var
a,b:array[0..600001,1..2] of longint;
z,dfn,low:array[0..50001] of longint;
p,bz:array[0..50001] of boolean;
n,m,t,i:longint;
ans:int64;
procedure qsort(i,j:longint);
var
l,r,mid:longint;
begin
l:=i;
r:=j;
mid:=a[(l+r) div 2,1];
repeat
while a[l,1]mid do dec(r);
if l<=r then
begin
a[0]:=a[l];
a[l]:=a[r];
a[r]:=a[0];
inc(l);
dec(r);
end;
until l>r;
if l0 then
begin
if bz[a[i,2]]=false then
begin
tarjan(a[i,2]);//如果节点i未被访问过继续向下找
low[x]:=min(low[x],low[a[i,2]]);
end
else if p[a[i,2]] then low[x]:=min(low[x],dfn[a[i,2]]);//如果节点i还在栈内
end;
if dfn[x]=low[x] then//如果x的最早访问时间等于其实际访问时间,则可把其视作回路的"始点"
begin
k:=0;//连通块长度
while (z[0]>0) and (z[z[0]]<>x) do//将由x直接或间接扩展出的点标记为同一连通块,标记访问后出栈
begin
p[z[0]]:=false;
inc(k);
dec(z[0]);
end;
p[x]:=false;
inc(k);
dec(z[0]);
ans:=ans+(k-1)*k div 2;
end;//如果节点x是强连通分量的根,退栈直到x的前一个数据,记录这个强连通分量的数据
end;
begin
readln(n,m);
for i:=1 to m do readln(a[i,1],a[i,2]);
qsort(1,m);
b[a[1,1],1]:=1;
for i:=2 to m do if a[i,1]<>a[i-1,1] then
begin
b[a[i-1,1],2]:=i-1;
b[a[i,1],1]:=i;
end;
b[a[m,1],2]:=m;//前向星
t:=0;
ans:=0;
for i:=1 to n do if bz[i]=false then tarjan(i);
writeln(ans);
end.
更短更快的模板
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fd(i,a,b) for(int i=a;i>=b;--i)
#define ll long long
using namespace std;
const int N=5e4+5,M=6e5+5;
int n,m,tp,num,time;
int v[N],in[N],z[N],dfn[N],low[N],last[N];
ll ans;
struct edge
{
int to,next;
}e[2*M];
void link(int x,int y)
{
e[++num]=(edge){y,last[x]},last[x]=num;
}
void Tarjan(int x)
{
dfn[x]=low[x]=++time,v[x]=1;
z[++tp]=x,in[x]=1;
for(int w=last[x];w;w=e[w].next)
{
int y=e[w].to;
if(!v[y]) Tarjan(y),low[x]=min(low[x],low[y]);
else if(in[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
int cnt=0;
while(tp&&z[tp]!=x) in[z[tp]]=0,++cnt,--tp;
in[z[tp]]=0,++cnt,--tp;
ans+=(ll)cnt*(cnt-1)/2;
}
}
int main()
{
freopen("tarjan.in","r",stdin);
freopen("tarjan.out","w",stdout);
scanf("%d%d",&n,&m);
fo(i,1,m)
{
int x,y;
scanf("%d%d",&x,&y);
link(x,y);
}
fo(i,1,n) if(!v[i]) Tarjan(i);
printf("%lld",ans);
}