2018 东北赛 Spin A Web (曼哈顿距离最小生成树)

7231: Spin A Web

时间限制: 1 Sec  内存限制: 128 MB
提交: 100  解决: 12
[提交] [状态] [讨论版] [命题人:admin]

题目描述

We have a canvas divided into grid with H rows and W columns. The square at the ith row from the top and the jth  column from the left is represented as (i, j). (i, j) square has two value xi,j  and yi,j .
Now we want to merge the squares to a connected web with minimal cost. Two squares can be connected if they are in the same row or column, and the cost of connecting (i0, j0) and (i1, j1) is
|xi0,j0 − xi1,j1 | + |yi0,j0 − yi1,j1 |.

 

输入

Input is given from Standard Input in the following format:
H W
x1,1 x1,2  . . . x1,W
.
.
xH,1 xH,2  . . . xH,W
y1,1 y1,2  . . . y1,W
.
.
yH,1 yH,2  . . . yH,W
Constraints
1 ≤ H × W ≤ 100000
−108 ≤ xi,j, yi,j ≤ 108(1 ≤ i ≤ H, 1 ≤ j ≤ W )
All of them are integers.

 

输出

Print one line denotes the minimal cost to merge the square to be a connected web.

 

样例输入

1 3
1 3 2
1 2 3

 

样例输出

5

 

来源/分类

2018东北四省赛 

 

这道题的AC之路特别艰辛,我到最后都不知道我原来的程序为什么跑步过去。。。。

这是一个曼哈顿距离最小生成树的题,我们当时参加了省赛,最后一个简单的题没有水出来,与银牌擦肩而过

拿了三等奖。。。

这是与省赛第二天的东北赛的题目,如今又重现了。看到当时的榜单貌似只有一个人做了出来,然而这道题我也不太清楚坑点

在哪里。

曼哈顿最小距离生成树的学习博客:

https://www.cnblogs.com/xzxl/p/7237246.html

 

说一下我的理解:

对于平面上的n个点我们要求最小生成树是要预处理两两之间的曼哈顿距离,时间花费是n*n的,但是由于两点之间的花费是

曼哈顿距离,我们就有了优化的方法,这里其实是由很多无用边存在的。我们筛掉无用边之后最多只有4*n条边这个时候我们

求最小生成树的时间复杂度就变成了n*logn。

例如 假设当前点的位置在原点,在y轴向右旋转45度的范围内的点,只会有一个点与位于原点这个点相连。

具体的证明在上边的博客。

设i(x1,y1) j(x2,y2)   设i位于原点则,j在R1范围内的条件是 y2-x2>y1-x1(斜率大于1),并且x2>x1,

两点之间的曼哈顿距离为 |y2-y1|+|x2-x1|  = y2+x2-(y1+x1)  由于y1+x1为定值,对于i点我们只需要求在

R1范围内 x>x1 并且 y-x> y1-x1 并且x+y最小的点。

如何实现:

1.对于x>x1 我们只需要按照x的值排序就能解决

2.如何保证 x+y最小, 对于每一个要查询的点 我们都查询位于他的横坐标之后的(y-x>要查询的)x+y最小的点

这一个可以用树状数组解决(树状数组维护一个(1-n)或者(i-n)的最大值,本题是维护了i到n的最大值)。

3.如何保证查询到的点的y-x>当前的y1-x1, 我们按照y-x的值排序,对于每一个a[i]的值(见下方代码)

pos之后的y-x肯定要比当前的y-x大,(位置可用二分来确定),又因为我们是倒叙遍历a[i]的,所以先插进去的点

都是在当前点后边的点,然后当前点pos的值我们更新为x+y并且记录id。

4.处理完R1区间还有R2 R3 ....,其实这些区间我们可以用区间变换把左边转移到R1区间,然后用处理R1区间的方法处理这些区间

,由于边是双向边,所以我们只需要处理位于原点(每个查询点都可以看作原点)右边的点。

具体的坐标变换过程如下:

对于第二步和第四步,我们按y=x翻转,即交换横纵坐标,第三部关于y轴对称一下。就将其他三个区间里的点都转换到R1范围内了。

 

对于本题有个限制就是只能合并同一行或者同一列,所以我们对每一行和每一列做一次曼哈顿距离就行。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
#define inf 2000000000
#define N 100015
#define lowb(x) (x&-x)
inline int read()
{
    char c;
    int sum=0;
    int f=1;
    c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        sum=sum*10+c-'0';
        c=getchar();
    }
    return sum*f;
}
int aa[N],cc[N];
 
void add(int p,int val,int id)
{
    while(p>0)
    {
        if(aa[p]>val)
        {
            aa[p]=val;
            cc[p]=id;
        }
        p-=lowb(p);
    }
}
int query(int p,int n)
{
    int m=inf;
    int ans=-1;
    while(p<=n)
    {
        if(aa[p]<m)
        {
            m=aa[p];
            ans=cc[p];
        }
        p+=lowb(p);
    }
    return ans;
}
struct node
{
    int x,y,id;
    friend bool operator <(node a,node b)
    {
        if(a.x!=b.x)return a.x<b.x;
        else return a.y<b.y;
    }
} p1[N];
 
struct Edg
{
    int u,v,k;
    friend bool operator <(Edg x,Edg y)
    {
        return x.k<y.k;
    }
} edg[8*N];
 
int a[N],b[N],p[N];
int tot;
 
bool cmp(int l,int r)
{
    if(p1[l].x!=p1[r].x)return p1[l].x<p1[r].x;
    else return p1[l].y<p1[r].y;
}
void creat(int  n)
{
    sort(p,p+n,cmp);
    for(int i=0; i<n; i++)
    {
        a[i]=b[i]=p1[p[i]].y-p1[p[i]].x;
    }
    sort(b,b+n);
    int kk=unique(b,b+n)-b;
    for(int i=1; i<=n; i++)aa[i]=inf,cc[i]=-1;
    for(int i=n-1; i>=0; i--)
    {
        int pos=lower_bound(b,b+kk,a[i])-b+1;
        int ans=query(pos,n);
        if(ans!=-1)
        {
            edg[tot].u=p1[p[i]].id;
            edg[tot].v=p1[p[ans]].id;
            edg[tot].k=abs(p1[p[i]].x-p1[p[ans]].x)+abs(p1[p[i]].y-p1[p[ans]].y);
            tot++;
        }
        add(pos,p1[p[i]].x+p1[p[i]].y,i);
    }
}
 
void Flip(int n)
{
    for(int i=0; i<4; i++)
    {
        for(int j=0; j<n; j++)
        {
            if(i==1||i==3)
            {
                swap(p1[p[j]].x,p1[p[j]].y);
            }
            else if(i==2)
            {
                p1[p[j]].x=-1*p1[p[j]].x;
            }
        }
        creat(n);
    }
}
 
int f[N];
 
int Find(int x)
{
    return f[x]==x?x:f[x]=Find(f[x]);
}
int w;
 
int getid(int i,int j)
{
 
    return i*w+j;
}
 
int main()
{
    //freopen("ce.txt","r",stdin);
    int h;
    h=read();
    w=read();
    int _id=1;
    for(int i=0; i<h; i++)
        for(int j=0; j<w; j++)p1[getid(i,j)].x=read(),p1[getid(i,j)].id=_id++;
    for(int i=0; i<h; i++)
        for(int j=0; j<w; j++)p1[getid(i,j)].y=read();
    for(int i=0; i<h; i++)
    {
        int cnt=0;
        for(int j=0; j<w; j++)p[cnt++]=getid(i,j);
        Flip(cnt);
    }
    for(int j=0; j<w; j++)
    {
        int cnt=0;
        for(int i=0; i<h; i++)p[cnt++]=getid(i,j);
        Flip(cnt);
    }
    for(int i=1; i<=h*w; i++)f[i]=i;
    sort(edg,edg+tot);
    LL cost=0;
    for(int i=0; i<tot; i++)
    {
 
        int f1=Find(edg[i].u);
        int f2=Find(edg[i].v);
        if(f1!=f2)
        {
            f[f2]=f1;
            cost+=edg[i].k;
        }
    }
    printf("%lld\n",cost);
    return 0;
}

附上原来一直超时的代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
#define inf 2000000000
#define N 100015
#define lowb(x) (x&-x)
inline int read()
{
    char c;
    int sum=0;
    int f=1;
    c=getchar();
    while(c<'0'||c>'9')
    {
        if(c=='-')f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9')
    {
        sum=sum*10+c-'0';
        c=getchar();
    }
    return sum*f;
}
struct BIT
{
    int a[N],c[N];
    void init(){
    for(int i=1;i<=N;i++)a[i]=inf,c[i]=-1;
    }
    void add(int p,int val,int id)
    {
        while(p>0)
        {
            if(a[p]>val)
            {
                a[p]=val;
                c[p]=id;
            }
            p-=lowb(p);
        }
    }
 
    int query(int p,int n)
    {
        int m=inf;
        int ans=-1;
        while(p<=n)
        {
            if(a[p]<m)
            {
                m=a[p];
                ans=c[p];
            }
            p+=lowb(p);
        }
        return ans;
    }
 
}T;
struct node
{
    int x,y,id;
    friend bool operator <(node a,node b)
    {
        if(a.x!=b.x)return a.x<b.x;
        else return a.y<b.y;
    }
}p1[N];
 
struct Edg
{
    int u,v,k;
    friend bool operator <(Edg x,Edg y)
    {
        return x.k<y.k;
    }
}edg[9*N];
 
int a[N],b[N],p[N];
int tot;
 
bool cmp(int l,int r)
{
    if(p1[l].x!=p1[r].x)return p1[l].x<p1[r].x;
    else return p1[l].y<p1[r].y;
}
void creat(int  n)
{
    sort(p,p+n,cmp);
    for(int i=0;i<n;i++)
    {
        a[i]=b[i]=p1[p[i]].y-p1[p[i]].x;
    }
    sort(b,b+n);
    int kk=unique(b,b+n)-b;
    T.init();
    for(int i=n-1;i>=0;i--)
    {
        int pos=lower_bound(b,b+kk,a[i])-b+1;
        int ans=T.query(pos,n);
        if(ans!=-1)
        {
            edg[tot].u=p1[p[i]].id;
            edg[tot].v=p1[p[ans]].id;
            edg[tot].k=abs(p1[p[i]].x-p1[p[ans]].x)+abs(p1[p[i]].y-p1[p[ans]].y);
            tot++;
        }
        T.add(pos,p1[p[i]].x+p1[p[i]].y,i);
    }
}
 
void Flip(int n)
{
    for(int i=0;i<4;i++)
    {
        for(int j=0;j<n;j++)
        {
            if(i==1||i==3)
            {
                swap(p1[p[j]].x,p1[p[j]].y);
            }
            else if(i==2)
            {
                p1[p[j]].x=-1*p1[p[j]].x;
            }
        }
        creat(n);
    }
}
 
int f[N];
 
int Find(int x)
{
    return f[x]==x?x:f[x]=Find(f[x]);
}
int w;
 
int getid(int i,int j)
{
 
    return i*w+j;
}
 
int main()
{
    //freopen("ce.txt","r",stdin);
    int h;
    scanf("%d%d",&h,&w);
    int _id=1;
    for(int i=0;i<h;i++)
        for(int j=0;j<w;j++)scanf("%d",&p1[getid(i,j)].x),p1[getid(i,j)].id=_id++;
    for(int i=0;i<h;i++)
        for(int j=0;j<w;j++)scanf("%d",&p1[getid(i,j)].y);
    for(int i=0;i<h;i++)
    {
        int cnt=0;
        for(int j=0;j<w;j++)p[cnt++]=getid(i,j);
        Flip(cnt);
    }
    for(int j=0;j<w;j++)
    {
        int cnt=0;
        for(int i=0;i<h;i++)p[cnt++]=getid(i,j);
        Flip(cnt);
    }
    for(int i=0;i<h*w;i++)f[i]=i;
    sort(edg,edg+tot);
    LL cost=0;
    for(int i=0;i<tot;i++)
    {
 
        int f1=Find(edg[i].u);
        int f2=Find(edg[i].v);
        if(f1!=f2)
        {
            f[f2]=f1;
            cost+=edg[i].k;
        }
    }
    printf("%lld\n",cost);
    return 0;
}

终于知道哪里出错了,我再也不把函数写在一个结构体里装X了。。。。

优化之后200多ms跑过。。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define LL long long
#define inf 2000000000
#define N 100015
#define lowb(x) (x&-x)
int aa[N],cc[N];
void add(int p,int val,int id)
{
    while(p>0)
    {
        if(aa[p]>val)
        {
            aa[p]=val;
            cc[p]=id;
        }
        p-=lowb(p);
    }
}
int query(int p,int n)
{
    int m=inf;
    int ans=-1;
    while(p<=n)
    {
        if(aa[p]<m)
        {
            m=aa[p];
            ans=cc[p];
        }
        p+=lowb(p);
    }
    return ans;
}

struct node
{
    int x,y,id;
    friend bool operator <(node a,node b)
    {
        if(a.x!=b.x)return a.x<b.x;
        else return a.y<b.y;
    }
} p1[N],p[N];

struct Edg
{
    int u,v,k;
    friend bool operator <(Edg x,Edg y)
    {
        return x.k<y.k;
    }
} edg[8*N];

struct Point
{
    int k,id;
    friend bool operator <(Point x,Point y)
    {
        return x.k<y.k;
    }
} b[N];
int AA[N];
int tot;
void creat(int  n)
{
    sort(p+1,p+1+n);
    for(int i=1; i<=n; i++)
    {
        b[i].k=p[i].y-p[i].x;
        b[i].id=p[i].id;
    }
    sort(b+1,b+1+n);
    for(int i=1; i<=n; i++)AA[b[i].id]=i,aa[i]=inf,cc[i]=-1;
    for(int i=n; i>=1; i--)
    {
        int pos=AA[p[i].id];
        int ans=query(pos,n);
        if(ans!=-1)
        {
            edg[tot].u=p[i].id;
            edg[tot].v=p[ans].id;
            edg[tot].k=abs(p[i].x-p[ans].x)+abs(p[i].y-p[ans].y);
            tot++;
        }
        add(pos,p[i].x+p[i].y,i);
    }
}

void Flip(int n)
{
    for(int i=0; i<4; i++)
    {
        for(int j=1; j<=n; j++)
        {
            if(i==1||i==3)
            {
                swap(p[j].x,p[j].y);
            }
            else if(i==2)
            {
                p[j].x=-p[j].x;
            }
        }
        creat(n);
    }
}

int f[N];
int Find(int x)
{
    return f[x]==x?x:f[x]=Find(f[x]);
}
int w;
int getid(int i,int j)
{

    return i*w+j;
}

int main()
{
    //freopen("ce.txt","r",stdin);
    int h;
    scanf("%d%d",&h,&w);
    int _id=1;
    for(int i=0; i<h; i++)
        for(int j=0; j<w; j++)scanf("%d",&p1[getid(i,j)].x),p1[getid(i,j)].id=_id++;
    for(int i=0; i<h; i++)
        for(int j=0; j<w; j++)scanf("%d",&p1[getid(i,j)].y);
    for(int i=0; i<h; i++)
    {
        int cnt=1;
        for(int j=0; j<w; j++)p[cnt++]=p1[getid(i,j)];
        Flip(cnt-1);
    }
    for(int j=0; j<w; j++)
    {
        int cnt=1;
        for(int i=0; i<h; i++)p[cnt++]=p1[getid(i,j)];
        Flip(cnt-1);
    }
    for(int i=1; i<=h*w; i++)f[i]=i;
    sort(edg,edg+tot);
    LL cost=0;
    for(int i=0; i<tot; i++)
    {

        int f1=Find(edg[i].u);
        int f2=Find(edg[i].v);
        if(f1!=f2)
        {
            f[f2]=f1;
            cost+=edg[i].k;
        }
    }
    printf("%lld\n",cost);
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值