关于标题
I(Interesting)T(Test)O(Of)N(Noip)
番外(论名字)
一道OJ的题:
Date
题目背景
SOURCE:NOIP2015-SHY-9
题目描述
小Y和小Z好不容易有机会相见啦,可是邪恶的小H却不想让他们相见。现在有一些城市,城市之间有双向路径相连,有路径相连的城市之间可以互相到达。小H可以任意选择一条路径,然后用他的邪恶力量污染这条路径,使得它不能被通行。虽然小Y和小Z在千辛万苦之后相遇了,但小Y非常害怕。她想让小Z告诉她,他们初始在哪些点对上,小H就可以选择一条路径污染使得他们不能相见。
注意:如果有一对点之间初始的时候就不联通,也是满足条件的,需要输出这对点。这是因为本来不联通,那么删一条边,当然也不联通。
输入格式
第一行两个数字 N 和 M 。N 表示城市数,M 表示路径数。
第二行到第 M+1 行,两个数 a 和 b。其中 1≤a,b≤N ,表示 a 和 b 之间有路径相连。
输出格式
输出一个整数,表示所求点对的数量
样例数据 1
输入 [复制]
2 1
1 2
输出
1
备注
【样例说明】
点对(1,2)满足不能相见的条件。
【数据范围】
对 30% 的输入数据 :1≤N≤100,1≤M≤200
对 100% 的输入数据 :1≤N≤20000,1≤M≤40000
按理说是改造一下Tarjan就能随便过的题。
然后。。。
TARJAN1.CPP
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
using namespace std;
#define MAXE 100050
#define MAXV 500050
using namespace std;
struct edge{int u,v,next,flag;}edges[MAXE];
int n,m,u,v;
long long ans=0;
int head[MAXV],nCount=0;
int low[MAXV],dfn[MAXV],belong[MAXV],stack[MAXV],top=0;
int cnt=1,tot=0;
int num[MAXV],outDegree[MAXV];
bool visit[MAXV];
void AddEdge(int U,int V)
{
nCount+=1;
edges[nCount].u=U;
edges[nCount].v=V;
edges[nCount].next=head[U];
edges[nCount].flag = false;
head[U]=nCount;
}
void tarjan(int u) //tarjan缩点
{
low[u]=dfn[u]=++cnt;
stack[++top]=u;
visit[u]=true;
for(int p=head[u];p;p=edges[p].next)
{
if(edges[p].flag)continue;
edges[p].flag = true;
edges[p^1].flag = true;
int v=edges[p].v;
if(!dfn[v])
{
tarjan(v);
low[u]=min(low[u],low[v]);
}
else if(visit[v])
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
tot++;
do
{
v=stack[top--];
belong[v]=tot;
num[tot]++;
visit[v]=false;
}
while(u!=v);
ans -= (long long)num[tot]*(num[tot]-1)>>1;
}
}
int main()
{
scanf("%d%d",&n,&m);
ans = n*(n-1)>>1;
for(int i=1;i<=m;i++)
{
scanf("%d%d",&u,&v);
AddEdge(u,v);
AddEdge(v,u);
}
for(int i=1;i<=n;i++)
if(!dfn[i])
tarjan(i);
cout << ans << endl;
return 0;
}
这是我用去年的模板改的,只过了两个点。。。。。。
下面是我用今年的模板改的:
TARJAN2.CPP
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
//Dijkstra
const int kkk = 200002;
int n,m,val,res=0;
long long ans=0;
int first[kkk];
struct node{int u,v,val,next,flag;}side[kkk*2];
int cnt=0,top=0,tot=0,cci=1;
void addedge(int u,int v,int val)
{
side[++cci].u = u;
side[cci].v = v;
side[cci].val = val;
side[cci].next = first[u];
side[cci].flag = false;
first[u] = cci;
}
bool visit[kkk];
int num[kkk],intedge[kkk],outedge[kkk];
int dfn[kkk],low[kkk],belong[kkk],stk[kkk];
void tarjan(int u)
{
low[u]=dfn[u]=++cnt;
stk[++top]=u; visit[u]=true;
for(int i=first[u];i;i=side[i].next)
{
if(side[i].flag)continue;
side[i].flag = true;
side[i^1].flag = true;
int v = side[i].v;
if(!dfn[v]){tarjan(v);low[u]=min(low[u],low[v]);}
else if(visit[v]){low[u]=min(low[u],dfn[v]);}
}
if(dfn[u]==low[u])
{
int v=-1;tot++;
while(u!=v)
{
v=stk[top--];
belong[v]=tot;
num[tot]++;
visit[v]=false;
}
ans -= (long long)num[tot]*(num[tot]-1)>>1;
}
}
int main()
{
cin >> n >> m;
for(int i=1;i<=m;i++)
{
int u,v;
cin >> u >> v;
addedge(u,v,1);
addedge(v,u,1);
}
ans = n*(n-1)>>1;
for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i);
cout << ans << endl;
return 0;
}
自己感受一下(T_T)(如果有有闲心的大佬帮我看一下第一段代码哪里错了。。。不胜感激。。。orz)
???
这道题用到了边双连通的求法。
点双连通的求法是求割点再把点拓展成边然后按边双连通的方法做就行。
详细见传送门:http://blog.csdn.net/fuyukai/article/details/51303292