URAL 1699 Turning Turtles 解题报告

题意:给定一个w*h图,'#'可以走,'.'不可以走,且保证任意两个可达的'#'之间有且只有一条路,给出q个询问(x1, y1, x2, y2),问位于(x1, y1)的'#'要到(x2, y2)的'#'要转过几个弯

思路:有题易知,图可以看成一棵一棵的树,问题就可以变成求树上两点间距离,很容易求出点(x1, y1)到其树根节点(x0, y0)需要转几个弯,现在要考虑的问题是:已知两点到根的“距离”和它们的公共祖先,如何求出它们之间的“距离”。

画图更利于理解:设公共祖先为f, 两点为u, v

1. 计算u和v到f的距离:如果公共祖先f在其父亲fa的左(右,上,下)边,而公共祖先f恰好也是要向左(右,上,下)边才能走到u,则它们之间的距离为dis[u]-dis[f],如下图绿点所示,起到f的距离为0; 否则,就为dis[u]-dis[f]-1,相当于减去fa到f的那次转弯,如下图红点所示,起到f的距离为1;v的计算同理

2. 合并u到f和f到v得路径,计算距离:记u在f的d1方向,v在f的d2方向,如果两方向垂直且u,v都不为公共祖先f,则dis(u, v)=dis(u, f)+dis(f, v)+1,相当于它们的路程在公共祖先f处应该拐一个弯,也可以用下图解释,绿点到红点的距离为2.

然后,又产生了问题——如何判断某点在其某一祖先的哪个方向?Moor采用的方法是记录下点的每个点的由其父亲转移过来的方向pdir,在tarjan的时候,记录下tarjan时每个点进入深搜时的方向nowd,在发现可以找到公共祖先时,这条路径上的每个点的nowd恰好对应了这条路径,可以利用这个判断;SS赛后也想了一个方法,在dfs的时候给标了一个号,这样可以保证按照dfs的原理,以某一点为根dfs下去,若其叶子的遍历顺序分别为(l1, l2, l3, ....),则点到l1路径上所有点的标号<=点到l2路径上所有点的标号<=点到l3路径上所有点的标号,利用这个性质,可以枚举四个方向判断点u,v在f的哪条链(方向)上,两个方法的速度是一样的......

上代码:Moor的解法

#include <iostream>
#include <cstdio>
#include <cstring>
#define MAXN 100010
using namespace std;
char **ma;
int **ind;
int fa[MAXN];
const int dir[4][2]={{0,1},{1,0},{0,-1},{-1,0}};
int w,h,he[MAXN],to[MAXN],nex[MAXN],co[MAXN],stop;
int nee[MAXN],ans[MAXN]={0};
int nowd[MAXN]={0},pdir[MAXN]={0};
bool vi[MAXN]={0};
inline bool check(int a,int b)
{
    if((a+1)%4==b||(a+3)%4==b)  return false;
    return true;
}
void dfs(int x,int y,int f,int num,int pre)
{
    nee[ind[x][y]]=num;
    pdir[ind[x][y]]=pre;
    for(int i=0;i<4;++i)
    {
        int nx=x+dir[i][0],ny=y+dir[i][1],nnum=num;
        if(ma[nx][ny]=='#'&&ind[nx][ny]!=f)
        {
            if(pre!=5&&!check(pre,i))
                ++nnum;
            dfs(nx,ny,ind[x][y],nnum,i);
        }
    }
}
void add(int a,int b,int c)
{
    to[stop]=b;
    nex[stop]=he[a];
    co[stop]=c;
    he[a]=stop++;
}
pair<int,int> mfind(int n)
{
    if(fa[n]!=n)
    {
        pair<int,int> pa=mfind(fa[n]);
        if(pa.second==1)    fa[n]=pa.first;
        else    pa.first=n,pa.second=1;
        return pa;
    }
    return make_pair(n,0);
}
void tarjan(int x,int y,int f,int pre)
{
    for(int i=0;i<4;++i)
    {
        int nx=x+dir[i][0],ny=y+dir[i][1];
        nowd[ind[x][y]]=i;
        if(ma[nx][ny]=='#'&&ind[nx][ny]!=f)
        {
            tarjan(nx,ny,ind[x][y],i);
            fa[ind[nx][ny]]=ind[x][y];
        }
    }
    for(int i=he[ind[x][y]];i!=-1;i=nex[i])
        if(vi[to[i]])
        {
            pair<int,int> tpa=mfind(to[i]);
            int ff=fa[tpa.first];
            ans[co[i]]=nee[ind[x][y]]+nee[to[i]]-2*nee[ff];
            if(ff!=ind[x][y])
            {
                if(check(pdir[tpa.first],nowd[ff]))
                    ans[co[i]]-=2;
                else    if(ff==1)   ++ans[co[i]];
            }
            else    if(pre!=5&&!check(pre,pdir[tpa.first]))
                ans[co[i]]-=1;
        }
    vi[ind[x][y]]=1;
}
int main()
{
    //freopen("/home/moor/Code/input.txt","r",stdin);
    scanf("%d%d",&w,&h);
    ma=new char* [w+3];
    ind=new int *[w+2];
    int top=1,q;
    stop=1;
    memset(he,-1,sizeof(he));
    for(int i=0;i<MAXN;++i) fa[i]=i;
    for(int i=0;i<=w+1;++i)
    {
        ma[i]=new char[h+3];
        ind[i]=new int[h+3];
        memset(ma[i],'\0',sizeof(char)*(h+3));
        memset(ind[i],0,sizeof(int)*(h+3));
    }
    for(int i=1;i<=w;++i)
    {
        scanf("%s",&ma[i][1]);
        for(int j=1;j<=h;++j)
        {
            if(ma[i][j]=='#')
                ind[i][j]=top++;
        }
    }
    scanf("%d",&q);
    for(int i=0;i<q;++i)
    {
        int a,b,xx1,xx2,yy1,yy2;
        scanf("%d%d%d%d",&xx1,&yy1,&xx2,&yy2);
        a=ind[xx1][yy1],b=ind[xx2][yy2];
        add(a,b,i+1);
        add(b,a,i+1);
    }
    for(int i=1;i<=w;++i)
        for(int j=1;j<=h;++j)
        {
            if(ma[i][j]!='#'||vi[ind[i][j]])    continue;
            dfs(i,j,-1,0,5);
            tarjan(i,j,-1,5);
        }
    for(int i=1;i<=q;++i)
        printf("%d\n",ans[i]);

    return 0;
}
SS的做法:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#define MAXN 100010
using namespace std;

char **ma;
int **ind;
int cnt, fa[MAXN];
const int dir[4][2]={{1,0},{0,1},{-1,0},{0,-1}};
int w, h, q, he[MAXN], to[MAXN], nex[MAXN], co[MAXN], stop;
int dis[MAXN], ans[MAXN];
int from[MAXN];
bool vi[MAXN];
struct node
{
    int x, y;
}id[MAXN];

void dfs(int x,int y,int f,int num,int pre)
{
    ind[x][y]=++cnt;
    id[cnt].x=x, id[cnt].y=y;
    dis[ind[x][y]]=num;
    from[ind[x][y]]=pre;
    for(int i=0;i<4;++i)
    {
        int nx=x+dir[i][0], ny=y+dir[i][1];
        if (ma[nx][ny]=='#'&&ind[nx][ny]!=f)
            dfs(nx,ny,ind[x][y],num+(pre!=5&&i!=pre), i);
    }
}
void add(int a,int b,int c)
{
    to[stop]=b;
    nex[stop]=he[a];
    co[stop]=c;
    he[a]=stop++;
}
int find(int x)
{
    if (fa[x]==x) return x;
    fa[x]=find(fa[x]);
    return fa[x];
}
void tarjan(int x,int y,int f)
{
    for (int i=0;i<4;++i)
    {
        int nx=x+dir[i][0], ny=y+dir[i][1];
        if (ma[nx][ny]=='#'&&ind[nx][ny]!=f)
        {
            tarjan(nx,ny,ind[x][y]);
            fa[ind[nx][ny]]=ind[x][y];
        }
    }
    vi[ind[x][y]]=1;
    for(int i=he[ind[x][y]];i!=-1;i=nex[i])
        if(vi[to[i]])
            ans[co[i]]=find(to[i]);
}
int cal(int f, int a, int *t)
{
    int tmp=-1;
    if (f==a) return 0;
    int res=dis[a]-dis[f];
    int x=id[f].x, y=id[f].y;
    for (int i=0; i<4; i++)
    {
        int xx=x+dir[i][0], yy=y+dir[i][1];
        if (ma[xx][yy]=='#' && ind[xx][yy]<=a && ind[xx][yy]!=f && ind[xx][yy]>tmp)
        {
            *t=i;
            tmp=ind[xx][yy];
        }
    }
    if (from[f]!=5&&(*t)!=from[f]) res--;
    return res;
}
int main()
{
    //freopen("data.in","r",stdin);
    scanf("%d%d",&w,&h);
    ma=new char* [w+3];
    ind=new int *[w+2];
    cnt=stop=0;
    memset(he,-1,sizeof(he));
    for(int i=0;i<MAXN;++i) fa[i]=i;
    for(int i=0;i<=w+1;++i)
    {
        ma[i]=new char[h+3];
        ind[i]=new int[h+3];
        memset(ma[i],'\0',sizeof(char)*(h+3));
        memset(ind[i],0,sizeof(int)*(h+3));
    }
    for(int i=1;i<=w;++i)
        scanf("%s",&ma[i][1]);
    scanf("%d",&q);
    for(int i=1;i<=w;i++)
        for(int j=1;j<=h;j++)
        {
            if (ma[i][j]!='#'||ind[i][j]) continue;
            dfs(i,j,-1,0,5);
        }
    for(int i=1;i<=q;++i)
    {
        int a,b,xx1,xx2,yy1,yy2;
        scanf("%d%d%d%d",&xx1,&yy1,&xx2,&yy2);
        a=ind[xx1][yy1], b=ind[xx2][yy2];
        add(a,b,i);
        add(b,a,i);
    }
    for(int i=1;i<=w;++i)
        for(int j=1;j<=h;++j)
        {
            if(ma[i][j]!='#'||vi[ind[i][j]])    continue;
            tarjan(i,j,-1);
        }
    stop=0;
    for(int i=1;i<=q;++i)
    {
        int a=to[stop++], b=to[stop++], f=ans[i], xa, xb;
        printf("%d\n", cal(f, a, &xa)+cal(f, b, &xb)+(f!=a&&f!=b&&((xa&1)!=(xb&1))));
    }
    return 0;
}

你可能感兴趣的:(URAL 1699 Turning Turtles 解题报告)