ITON - 2 论名字

关于标题

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值