JZOJ1418. 【COCI2007】追捕盗贼

题目描述

Description
  为了帮助警察抓住在逃的罪犯,你发明了一个新的计算机系统。警察控制的区域有N个城市,城市之间有E条双向边连接,城市编号为1到N。
  警察经常想在罪犯从一个城市逃亡另一个城市的过程中抓住他。侦查员在仔细研究地图,以决定在哪个城市设置障碍,或者断掉某条路。你的计算机系统必须回答以下两种问题:
  1、 如果连接城市G1和G2的路被封掉,罪犯能否从城市A逃到城市B?
  2、 如果城市C被封掉,罪犯能否从城市A逃到城市B?
  编程实现上述计算机系统。

Input
  输入第一行包含两个整数N和E(2<=N<=100000,1<=E<=500000),表示城市和边的数量。
  接下来E行,每行包含两个不同的整数Ai和Bi,表示城市Ai和Bi之间有一条边直接相连,任意两个城市之间最多只有一条边相连。
  接下来一行包含一个整数Q(1<=Q<=300000),表示询问次数。
  接下来Q行,每行包含4个或5个整数,第一个数为1或2,表示询问问题的种类。
  如果问题种类是1,后面跟着4个整数A,B,G1,G2,如上描述表示询问如果G1和G2之间的路封掉罪犯能否从城市A逃到城市B,保证A和B不同,G1和G2之间一定存在路。
  如果问题种类是2,后面跟着三个整数A,B,C,三个数互不相同,表示询问如果封掉城市C,罪犯能否从城市A逃到城市B。
  输入数据保证一开始任意两个城市都可以相互到达。
Output
  每个询问输出一行“yes”或“no”。

Sample Input
13 15
1 2
2 3
3 5
2 4
4 6
2 6
1 4
1 7
7 8
7 9
7 10
8 11
8 12
9 12
12 13
5
1 5 13 1 2
1 6 2 1 4
1 13 6 7 8
2 13 6 7
2 13 6 8
Sample Output
yes
yes
yes
no
yes

题解

其实分开来搞不难……

询问1

先Tarjan缩环(无向图强连通分量,每次走过一条边就不走其反向边),之后如果删的边在一个强连通分量里面,那就不会有影响。
否则用dfs序判一下

询问2

在Tarjan的dfs树上搞。

如果c能影响到a~b的点,那么c一定在a~b的路径上,或者c是a or b的祖先且ab的lca是c的祖先。
设该点到c的路径上最靠近c的点(就是c的儿子之一)为x
如果一个点在删掉c后还能连通,那么x的子树中一定有一个点能走返祖边到c的祖先。

然后判断low[x]和dfn[c]就行了。

code

#include 
#include 
#include 
#include 
#include 
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define min(a,b) (a
using namespace std;

int a[1000002][3];
struct BB{int x,y,z;} b[1000002];
int ls[100001];
int Ls[100001];
int dfn[100001];
int low[100001];
int Dfn[100001][2];
int Df[100001][2];
int num[100001];
int fa[100001][17];
bool bz[100001];
bool Bz[500001];
int d[100001];
int D[100001];
int dp[100001];
int N,n,m,i,j,k,l,len,L,Q,s1,s2,s3,s4,s5,S;

void swap(int &x,int &y)
{
    int z=x;
    x=y;
    y=z;
}

bool cmp(BB a,BB b) {return a.xvoid New(int x,int y)
{
    len++;
    a[len][0]=y;
    a[len][1]=ls[x];
    a[len][2]=x;
    ls[x]=len;
}

void Read()
{
    len=1;
    scanf("%d%d",&n,&m);
    fo(i,1,m)
    {
        scanf("%d%d",&j,&k);
        New(j,k);New(k,j);
    }
}

void tj(int t)
{
    int i;

    Df[t][0]=++S;
    fo(i,1,16)
    fa[t][i]=fa[fa[t][i-1]][i-1];

    dfn[t]=++j;
    low[t]=j;
    d[++L]=t;
    bz[t]=1;

    for (i=ls[t]; i; i=a[i][1])
    if (!Bz[i>>1])
    {
        Bz[i>>1]=1;

        if (!dfn[a[i][0]])
        {
            fa[a[i][0]][0]=t;
            dp[a[i][0]]=dp[t]+1;
            tj(a[i][0]);
        }
        if (bz[a[i][0]]) low[t]=min(low[t],low[a[i][0]]);

        Bz[i>>1]=0;
    }

    Df[t][1]=S;

    if (dfn[t]==low[t])
    {
        k++;
        for (; d[L+1]!=t; num[d[L--]]=k) bz[d[L]]=0;
    }
}

void dfs(int t,int Fa)
{
    Dfn[t][0]=++j;

    for (int i=Ls[t]; i; i=b[i].z)
    if (b[i].y!=Fa)
    dfs(b[i].y,t);

    Dfn[t][1]=j;
}

bool Inc(int x,int y) {return Dfn[x][0]<=Dfn[y][0] && Dfn[y][1]<=Dfn[x][1];}
bool inc(int x,int y) {return Df[x][0]<=Df[y][0] && Df[y][1]<=Df[x][1];}

int lca(int i,int j)
{
    int k;

    if (dp[i]for (k=16; dp[i]>dp[j]; i=(dp[fa[i][k]]>=dp[j])?fa[i][k]:i,k--);

    fd(k,16,0) if (fa[i][k]!=fa[j][k]) i=fa[i][k],j=fa[j][k];
    if (i!=j) i=fa[i][0];

    return i;
}

bool pd(int s1,int s2)
{
    int i,k;

    if (inc(s2,s1))
    {
        for (k=16,i=s1; k>=0; i=(dp[fa[i][k]]>dp[s2])?fa[i][k]:i,k--);
        if (low[i]>=dfn[s2])
        {
            printf("no\n");
            return 1;
        }
        return 0;
    } else return 0;
}

void Sort()
{
    fo(i,2,len)
    b[i].x=num[a[i][2]],b[i].y=num[a[i][0]];
    stable_sort(b+2,b+len+1,cmp);
    j=0;
    k=len;
    fo(i,2,len)
    if (b[i].x!=b[i].y && (b[i].x!=b[i-1].x || b[i].y!=b[i-1].y))
    {
        j++;
        b[j].x=b[i].x;
        b[j].y=b[i].y;
        b[j].z=Ls[b[j].x];
        Ls[b[j].x]=j;
    }
    len=j;
}

void work1()
{
    scanf("%d",&s5);
    s2=num[s2];s3=num[s3];s4=num[s4];s5=num[s5];

    if (!Inc(s4,s5)) swap(s4,s5);
    if ((Inc(s5,s2) && !Inc(s5,s3) || !Inc(s5,s2) && Inc(s5,s3)) && s4!=s5)
    printf("no\n"); else printf("yes\n");
}

void work2()
{
    l=lca(s2,s3);

    if (inc(s4,l) && s4!=l || !inc(s4,s2) && !inc(s4,s3)) printf("yes\n");
    else
    {
        if (pd(s2,s4)) return;
        if (pd(s3,s4)) return;

        printf("yes\n");
    }
}

void Tarjan()
{
    j=0;L=0;k=0;
    tj(1);
    N=k;
}

int main()
{
    Read();
    Tarjan();
    Sort();

    j=0;dfs(1,0);

    for (scanf("%d",&Q); Q; Q--)
    {
        scanf("%d%d%d%d",&s1,&s2,&s3,&s4);
        if (s1==1) work1(); else work2();
    }

    return 0;
}

你可能感兴趣的:(OJ题解,树上算法)