HDU 4338 Simple Path 点双连通+lca

【题目大意】

给你一个无向图。问,从点u到点v,若是只走简单路径,有多少个点不能到达?

【思路】

这题肯定是要双连通缩点的,以前老是觉得点双连通不会用来缩点,因为割点可以属于多个连通集合,现在立马打脸了 o(╯□╰)o

最开始用,边-双连通思考,然后发现下面这种图就....


如果我们询问(6,7)和(6,4),显然两次点6所属集团的意义不同;除此之外,比方(1,2,3)这个集团,直接合并也是不合理的,询问(1,3)和(3,7),点3的意义也是不同的

问题的关键,其实关键就是割点。如果对点双连通比较熟悉的话,应该能想到:除了孤点,任何点都会至少属于一个连通集合,而只有割点能属于2个及以上的集合。连通集合一定是通过割点相连的。

所以,我们可以考虑把原图改成通过割点相连的形式。上面那个图就可以改成


显然,每条边的一段都是连通集合,而另一段是割点。

仔细分析割点到割点、非割点到非割点、割点到非割点三种情况,你会发现,点u到点v,能走的点就是修改后的图(这个图不会有环)对应的简单路径上的所有的点。比方(2,5)能经过的点就为 {2}∪{1,2,3}∪{3}∪{3,5}∪{5} == {1,2,3,5} ,(2,6)的为 {2}∪{1,2,3}∪{3}∪{3,5}∪{5}∪{4,5,6} == {1,2,3,4,5,6}。 

因为路径上的点是有重复的,我们仍需要思考一下。实际上,点的数目,等于每个点的点的数目减去路径上的边数。

而具体怎么去求无环图上一个简单路径上的点数和以及边数和。这里可以采用多种方法,我这里用的lca维护一下。


P.S. 数据好像不严,询问中有点的编号>=n的情况,我这种情况是直接输出n

#pragma comment(linker, "/STACK:102400000,102400000")
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<cctype>
#include<string>
#include<algorithm>
#include<iostream>
#include<ctime>
#include<map>
#include<set>
using namespace std;
#define MP(x,y) make_pair((x),(y))
#define PB(x) push_back(x)
typedef __int64 LL;
//typedef unsigned __int64 ULL;
/* ****************** */
const LL INF = 1LL<<55;
const double INFF = 1e100;
const double eps = 1e-8;
const LL mod = 10000000007LL;
const int NN = 100010;
const int MM = 400010;
/* ****************** */

int dfn[NN], low[NN], tsp;
int sta[MM], sta_top;
int id[NN], num[NN*2], id_cnt;
int color[NN];
vector<int>bccno[NN];
struct G
{
    int u, v, next;
}E[MM], E1[MM];
int p1[NN], T1;
int p[NN*2], T;

int long2[NN*2*2];
int deep[NN*2], pos[NN*2], dian[NN*2];
int oula[NN*2*2];
int rmq[NN*2*2][20];

void init_long2(int n)
{
    int i;
    long2[1]=0;
    for(i=2;i<=n;i++)
    {
        long2[i]=long2[i-1]+(i==(i&(-i)));
    }
}

void add(int u,int v,G *E,int *p,int &T)
{
    E[T].u = u;
    E[T].v = v;
    E[T].next = p[u];
    p[u] = T++;
}

void bcc(int u,int fa,int col)
{
    int i, ii, v;
    dfn[u] = low[u] = ++tsp;
    color[u] = col;
    for(i = p1[u]; i + 1; i = E1[i].next)
    {
        v = E1[i].v;
        if(dfn[v]==0)
        {
            sta[++sta_top] = i;
            bcc(v, u, col);
            low[u] = min(low[u], low[v]);
            if(low[v] >= dfn[u])
            {
                num[++id_cnt] = 0;
                for(;;)
                {
                    ii = sta[sta_top--];
                    if(id[ E1[ii].u ] != id_cnt)
                    {
                        bccno[ E1[ii].u ].PB(id_cnt);
                        num[id_cnt] ++;
                        id[ E1[ii].u ] = id_cnt;
                    }
                    if(id[ E1[ii].v ] != id_cnt)
                    {
                        bccno[ E1[ii].v ].PB(id_cnt);
                        num[id_cnt] ++;
                        id[ E1[ii].v ] = id_cnt;
                    }
                    if(E1[ii].u == u && E1[ii].v == v)
                        break;
                }
            }
        }
        else if(dfn[v]<dfn[u] && v != fa)
        {
            sta[++sta_top] = i;
            low[u] = min(low[u], dfn[v]);
        }
    }
}
//生成欧拉序列,计算每个节点深度,每个点首次出现的位置
//其第一个父亲,没有用-1表示
void dfs(int u,int fa,int cen)
{
    oula[++tsp]=u;
    deep[u]=cen;
    pos[u]=tsp;

    int i,v;
    for(i=p[u];i+1;i=E[i].next)
    {
        v=E[i].v;
        if(v!=fa)
        {
            dian[v] = dian[u] + num[v];
            dfs(v,u,cen+1);
            oula[++tsp]=u;
        }
    }
}
//用于lca的rmq
void init_rmq(int n)
{
    int i,j,en,len;
    int t1,t2;

    for(i=1;i<=n;i++)
        rmq[i][0]=i;

    for(j=1;j<=long2[n];j++)
    {
        en=n+1-(1<<j);
        len=1<<(j-1);
        for(i=1;i<=en;i++)
        {
            t1=oula[ rmq[i][j-1] ];
            t2=oula[ rmq[i+len][j-1] ];
            if(deep[t1]<deep[t2])
                rmq[i][j]=rmq[i][j-1];
            else
                rmq[i][j]=rmq[i+len][j-1];
        }
    }
}
int ask_lca(int u,int v)
{
    int st=pos[u];
    int en=pos[v];
    if(st>en)
        swap(st,en);
    int k=long2[en-st+1];
    int id1=oula[ rmq[st][k] ];
    int id2=oula[ rmq[en+1-(1<<k)][k] ];

    if(deep[id1]<deep[id2])
        return id1;
    return id2;
}

void solve(int n,int sum_n)
{
    int m,i,u,v,ans;
    tsp=0;
    for(i=1;i<=n;i++)
        pos[i] = -1;
    for(i=1;i<=n;i++)
    {
        if(pos[i]==-1)
        {
            dian[i] = num[i];
            dfs(i,-1,0);
        }
    }
    init_rmq(tsp);
    scanf("%d",&m);
    while(m--)
    {
        scanf("%d%d",&u,&v);
        if(u>=sum_n || v>=sum_n)
        {
            printf("%d\n",sum_n);
         //   while(1);
            continue;
        }

        u++, v++;
        if(u==v)
            printf("%d\n", sum_n-1);
        else if(color[u]!=color[v])
            printf("%d\n", sum_n);
        else
        {
            u = id[u];
            v = id[v];
            i = ask_lca(u, v);
            ans = dian[u] + dian[v] - dian[i]*2 + num[i];
            ans -= (deep[u]+deep[v]-deep[i]*2);
            ans = sum_n - ans;
            printf("%d\n",ans);
        }
    }
    puts("");
}

int main()
{
    init_long2(100000*4);
    int n, m, i, j, u, v,  si;
    int ee = 0;
    while(scanf("%d%d", &n, &m) != EOF)
    {
        memset(p1, -1, sizeof(p1));
        T1 = 0;
        for(i = 0; i < m; i ++)
        {
            scanf("%d%d", &u, &v);
            u ++, v ++;
            add(u, v, E1, p1, T1);
            add(v, u, E1, p1, T1);
        }
        memset(dfn, 0, sizeof(dfn));
        memset(id, -1, sizeof(id));
        tsp = 0;
        id_cnt = 0;
        sta_top = 0;
        for(i = 1; i <= n; i ++)
        {
            if(dfn[i]==0)
            {
                bcc(i, -1, i);
            }
        }
        memset(p, -1, sizeof(p));
        T = 0;
        for(i = 1; i <= n; i ++)
        {
            si = bccno[i].size();
            if(si > 1)
            {
                u = ++id_cnt;
                id[i] = u;
                num[u] = 1;
                for(j = 0; j < si; j ++)
                {
                    v = bccno[i][j];
                    add(u, v, E, p, T);
                    add(v, u, E, p, T);
                }
            }
            bccno[i].clear();
        }
        printf("Case #%d:\n",++ee);
        solve(id_cnt, n);
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值