Description
你是活跃在历史的幕后的一名特工,为了世界的和平而日以继夜地努力着。
这个世界有N个国家,编号为1…N,你的目的是在这N个国家之间建立尽可能多的友好关系。你为了制定一个特工工作的计划,作出了一张当今国际关系的示意图。
你准备了一张非常大的画纸,先画下了代表每个国家的N个点。接下来,为了表示现在的国际关系,画下了M个连接两个国家的有向边,其中从国家a连向国家b的有向边(下面称作“边(a,b)”)表示“现在国家a向国家b派遣了大使”。这样就做出了N个点M条边的当今国际关系示意图。
作为两国友好关系的开端,两国之间需要进行“友好条约缔结会议”(以下简称会议)。如果某两个国家p和q要进行会议,那么需要一个向两国都派遣了大使的国家x作为中介。会议结束后,会议的双方相互向对方的国家派遣大使。换句话说,为了让国p和国q进行会议,必须存在一个国家x满足边(x,p)和边(x,q)都存在,并且在会议后添加两条边(p,q)和(q,p)(如果需要添加的某条边已经存在则不添加)。
你的工作是对于可以进行会议的两国,选择会议的中介并促使会议进行。使用这张图进行工作的模拟的话,世界距离和平还有多远的一个重要的基准就是这张图上的边数。也就是说,你想知道反复进行【选择两个国家使其进行会议】的工作后,图上的边数最多会到达多少。
现在给出国家的个数以及当今国际关系的情报,请你求出反复进行【选择两个国家使其进行会议】的工作后,图上的边数最多会到达多少。
Input
第一行两个空格分隔的整数N和M,分别表示世界上国家的个数和图中的边数
接下来M行描述画纸上的有向边的信息,其中第i行(1<=i<=M)有两个空格分隔的整数Ai和Bi,表示图中有一条从Ai到Bi的有向边(即Ai国向Bi国派遣了大使)。
Output
输出一行一个整数,表示能实现的边数的最大值。注意这个边数包括原有的边数和新连接的边数。
Sample Input
5 4
1 2
1 3
4 3
4 5
Sample Output
10
HINT
按照下面的顺序实现10条边:
以国1为中介,国2与国3进行会议;
以国4为中介,国3与国5进行会议;
以国3为中介,国2与国5进行会议。
1<=N<=10^5
1<=M<=2*10^5
1<=Ai<=N(1<=i<=M)
1<=Bi<=N(1<=i<=M)
Ai≠Bi(1<=i<=M)
(Ai,Bi)≠(Aj,Bj)(1<=i<j<=M)
Source
JOI 2013~2014 春季training合宿 竞技2 By PoPoQQQ
分析:
假设有一个点
p
p
p,显然他连出去的点
a
1
,
a
2
,
.
.
.
.
,
a
n
a_1,a_2,....,a_n
a1,a2,....,an将会连成一个完全子图。
然后把大于
1
1
1的完全子图的节点插入一个队列里。
枚举队列里的点,如果存在
x
x
x连向
y
y
y的一条边,显然
x
x
x中的所有点都可以有连向
y
y
y的双向边,所以可以合并
x
x
x和
y
y
y。如果
y
y
y是一个大小为
1
1
1的完全子图,则把他插入队列。
代码:
/**************************************************************
Problem: 4243
User: liangzihao
Language: C++
Result: Accepted
Time:4904 ms
Memory:8904 kb
****************************************************************/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <queue>
#define LL long long
const int maxn=2e5+7;
using namespace std;
int n,m,x,y,cnt,pl;
int ls[maxn],vis[maxn],size[maxn],p[maxn];
LL ans;
struct edge{
int y,next;
}g[maxn];
queue <int> q;
void add(int x,int y)
{
g[++cnt]=(edge){y,ls[x]};
ls[x]=cnt;
}
int find(int x)
{
if (!p[x]) return x;
return p[x]=find(p[x]);
}
void uni(int x,int y)
{
int u=find(x),v=find(y);
if (u==v) return;
p[u]=v;
size[v]+=size[u];
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
}
for (int i=1;i<=n;i++)
{
p[i]=0;
size[i]=1;
}
for (int i=1;i<=n;i++)
{
pl=0;
for (int j=ls[i];j>0;j=g[j].next)
{
int y=g[j].y;
if (!pl) pl=y;
else
{
uni(pl,y);
if (!vis[y]) q.push(y),vis[y]=1;
if (!vis[pl]) q.push(pl),vis[pl]=1;
}
}
}
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=ls[x];i>0;i=g[i].next)
{
int y=g[i].y;
uni(x,y);
if (!vis[y]) q.push(y),vis[y]=1;
}
}
for (int i=1;i<=n;i++)
{
if (find(i)==i)
{
ans+=(LL)size[i]*((LL)size[i]-1);
}
for (int j=ls[i];j>0;j=g[j].next)
{
int y=g[j].y;
if (find(i)!=find(y)) ans++;
}
}
printf("%lld\n",ans);
}