HDU 4338 Simple Path 点双连通+lca

来源:互联网 发布:中航技国际经贸知乎 编辑:程序博客网 时间:2024/04/28 12:56

【题目大意】

给你一个无向图。问,从点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的rmqvoid 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;}


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 20岁白头发很多怎么办 一天掉40根头发怎么办 烫完头发掉头皮怎么办 接发遗留的胶水怎么办 头发又干又卷怎么办 每天掉很多头发怎么办掉头发 植过发15天手抓植发区了怎么办 洗头梳头老掉头发怎么办 掉头发特别特别严重怎么办 哺乳期掉头发特别严重怎么办 最近掉头发特别严重怎么办 近掉头发特别严重怎么办 50多岁脱发严重怎么办 2岁宝宝掉发严重怎么办 生孩子后掉头发严重怎么办 有16岁孩孑教吾听怎么办 生完孩子后脱发怎么办 学生掉头发很厉害怎么办 35岁开始掉头发怎么办 高三学生玩手机怎么办 高三学生不学习怎么办 17岁经常掉头发怎么办 出汗后头皮很痒怎么办 头发老是掉怎么办会不会长出来 头发痒还掉头发怎么办 7个月宝宝入睡难怎么办 45天宝宝入睡难怎么办 两岁宝宝入睡难怎么办 吃激素掉发严重怎么办 20多岁掉头发怎么办 最近头发老掉怎么办20 20多岁最近严重脱发怎么办 头痒头皮屑多掉头发怎么办 有头屑头痒掉头发怎么办 老是头痒掉头发怎么办 头屑头痒掉头发怎么办 头痒头屑多掉头发怎么办 洗头发时总是掉发怎么办 染头发染到脸上洗不掉怎么办 总爱掉头发怎么办20岁 头发开叉长的慢怎么办