【NOIP模拟】夕阳

“我有个愿望,我希望在灿烂千阳时遇见你。”

是啊,我也希望。

题面

这是个有n个点的世界,有m条无向边连接着这n个点,但是不保证点之间能够互相到达。

“这个世界的夕阳,只在奇数长的简单路径的尽头。”一个神如是说。

于是我想知道对于一个点对(x,y),x到y之间的所有简单路径中是否存在长度为奇数的路径,只有这样,我才能找到存在有夕阳的路。

对于50%的数据,1≤n,m,q≤500

对于100%的数据,,1≤n,q,m≤100000

分析

比较困难的是到底怎么判这个奇边呢?

多画图,会发现,u和v之间有奇边无非两种情况。

1.dis{u,v}&1==0

2.dis{u,v}&1==1,但是u和v的路径上,有一条边在一个奇环内。意思是,如果一个奇环边数是5,本来u,v之间的距离是4,可以从奇环绕着走,距离变为4-1+5-1=7。

所以,在计算点双联通分量的同时,一旦遇到了返祖边,就判断此环是否是奇环,如果是,退栈的过程中将这个环的所有点都标记上。

最后用lca可求两点距离,用于判断情况1。同时我们维护了每个点在多少个奇环内,也可以通过lca的奇环数量与u,v两点的奇环数量,判断u,v是否有边在奇环内。

代码

#include<bits/stdc++.h>
using namespace std;
#define N 100100
int n,m,op,ans,cnt,gro,tot,top,tim;
int fa[N][20],first[N],col[N],dfn[N],low[N];
int s[N],sta[N],ins[N],odd[N],dep[N];
struct email
{
    int u,v;
    int nxt;
}e[N*4];
queue<int>q;
inline void add(int u,int v)
{
    e[++cnt].nxt=first[u];first[u]=cnt;
    e[cnt].u=u;e[cnt].v=v;
}
inline void read(int &x)
{
    x=0;int fh=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')fh=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    x*=fh;
}


void tarjan(int u,int pre)
{
    dfn[u]=low[u]=++tot;
    sta[++top]=u;ins[u]=1;
    for(int i=1;i<=tim;i++)
        fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=first[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(v==pre)continue;
        if(!dfn[v])
        {    
            fa[v][0]=u;
            dep[v]=dep[u]+1;
            tarjan(v,u);
            low[u]=min(low[u],low[v]);
        }
        else
            if(ins[v])
            {
                low[u]=min(low[u],dfn[v]);
                if(dep[u]%2==dep[v]%2)odd[u]=1;
            }
    }
    ins[u]=0;
    if(dfn[u]==low[u])
    {
        int pos=top,w=0;
        while(sta[pos]!=u)w|=odd[sta[pos--]];
        if(w)
        {
            for(int i=pos+1;i<=top;i++)    
                s[sta[i]]++;
        }
        gro++;
        for(int i=pos;i<=top;i++)
            col[sta[i]]=gro,ins[sta[i]]=0;
        top=pos-1;
    }
}

inline int lca(int x,int y)
{
    if(dep[x]<dep[y])
        swap(x,y);
    int t=dep[x]-dep[y];
    for(int i=0;(1<<i)<=t;i++)
        if(t&(1<<i))
            x=fa[x][i];
    if(x==y)return x;
    for(int i=19;i>=0;i--)
        if(fa[x][i]!=fa[y][i])
            x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}


void dfs(int u)
{
    for(int i=first[u];i;i=e[i].nxt)
    {
        int v=e[i].v;
        if(fa[v][0]==u)    
        {
            s[v]+=s[u];
            dfs(v);
        }
    }
}


int main()
{
    read(n);read(m);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        read(u);read(v);
        add(u,v);add(v,u);
    }
    tim=log(n)/log(2)+1;
    for(int i=1;i<=n;i++)
        if(!col[i])
        {
            dep[i]=1;
            fa[i][0]=i;
            tarjan(i,i);
        }    
    for(int i=1;i<=n;i++)
        if(fa[i][0]==i)
            dfs(i);
    read(op);
    for(int i=1;i<=op;i++)
    {
        int u,v;
        read(u);read(v);
        if(fa[u][tim]!=fa[v][tim])
            printf("No
");
        else
        {
            int pref=lca(u,v);
            if ((dep[u]+dep[v]-dep[pref]*2)%2==1||s[u]+s[v]-s[pref]*2>0)
                printf("Yes
");
            else printf("No
");
        }    
    }
    return 0;
    
}

送一组考场上造的小样例

input
23 22
1 2
1 3
3 9
8 9
2 4
4 5
5 8
5 6
5 7
10 11
11 12
14 15
15 16
16 17
14 17
18 19
18 20
19 20
20 21
20 22
21 23
22 23
11
10 20 
12 8 
2 4 
7 9 
1 5 
1 7 
13 13 
1 1 
20 19 
14 18 
4 9 

output
No
No
Yes
Yes
Yes
Yes
No
No
Yes
No
Yes
“Make my parents proud,and impress the girl I like.”
原文地址:https://www.cnblogs.com/NSD-email0820/p/9799050.html