题意:
给你一个n*m的方块,每一个1*1的小方块可以与相邻的右边、下边之间建一座围墙。
D a,代表该小方块与下面的方块之间建立围墙需要a花费
R b,代表该小方块与右边的方块之间建立围墙需要a花费
X 0表示无法与某一(右/下)个方向建立围墙
然后魔王设计了一个迷宫,使得迷宫内任意不同的两个点都有且只有一条简单路径,并且保证花费最小
(简单路径:如果一条路径上的顶点除了起点和终点可以相同外,其它顶点均不相同,则称此路径为一条简单路径)
然后q个询问,给你两个点,问你根据上面那个迷宫,这两点之间的最短距离是多少?
解析:
一开始看这个题目,我自己也是迷迷糊糊的....这个迷宫应该是给你各个小方块造围墙的花费后就已经确定了。
然后我就开始一直乱花....找点这种迷宫的规律,后来还真找到了....
把每一个小方块看作点,我们其实就是找一个图的最小生成树!!!
后来想一想一确实是这样,应该看到这个性质就联想到树的,任意两个点有且只有一条简单路径,这个就是树的一个重要性质
然后他要保证删掉的边最小,那么就是使保留的边最大,建立负权边跑一边Kruskal,O(eloge)
之后就是询问两点的距离,其实就是树上两点的距离,那么用树上倍增法求lca来做就可以了,树链剖分也可以,就使写起来有点麻烦,所以我特地去学了一下树上倍增——好东西!O(nlogn)
最后的复杂度是O(eloge+nlogn)
# include<iostream>
# include<cstdio>
# include<cstring>
# include<algorithm>
#include <vector>
typedef long long ll;
using namespace std;
const int M = 250000+100;
const ll INF=0x3f3f3f3f3f3f3f3f;
struct edge
{
int fr,to,nxt;
ll w;
};
bool cmp(edge a,edge b)
{
return a.w<b.w;
}
int pre[M],head[M],cnt;
edge e[M*4];
vector<int> ee[M];
int max0=23;
int fa[M][25],dep[M];
int data[M][25];
/*int father(int i,int k)
{
for(int x=0;x<=int(log2(k));x++)
if((1<<x)&k) //(1<<x)&k可以判断k的二进制表示中,第(x-1)位上是否为1
i=fa[i][x]; //把i往上提
return i;
}*/
void dfs(int x)
{
for(int i=1;i<=max0;i++)
if(fa[x][i-1]) //在dfs(x)之前,x的父辈们的fa数组都已经计算完毕,所以可以用来计算x
{
fa[x][i]=fa[fa[x][i-1]][i-1];
if(fa[x][i])
data[x][i]=data[x][i-1]+data[fa[x][i-1]][i-1];
}
else break; //如果x已经没有第2^(i-1)个父亲了,那么也不会有更远的父亲,直接break
for(int j=0;j<ee[x].size();j++)
{
int i=ee[x][j];
if(i!=fa[x][0]) //如果i不是x的父亲就是x的儿子
{
data[i][0]=1;
fa[i][0]=x; //记录儿子的第一个父亲是x
dep[i]=dep[x]+1; //处理深度
dfs(i);
}
}
}
int LCA(int u,int v)
{
int ans=0;
if(dep[u]<dep[v])swap(u,v); //我们默认u的深度一开始大于v,那么如果u的深度小就交换u和v
int delta=dep[u]-dep[v]; //计算深度差
for(int x=0;x<=max0;x++) //此循环用于提到深度相同。
if((1<<x)&delta)
{
ans+=data[u][x];
u=fa[u][x];
}
if(u==v)return ans;
for(int x=max0;x>=0;x--) //注意!此处循环必须是从大到小!因为我们应该越提越“精确”,
if(fa[u][x]!=fa[v][x]) //如果从小到大的话就有可能无法提到正确位置,自己可以多想一下
{
ans+=data[u][x];
ans+=data[v][x];
u=fa[u][x];
v=fa[v][x];
}
//return fa[u][0]; //此时u、v的第一个父亲就是LCA。
ans+=data[u][0];
ans+=data[v][0];
return ans;
}
void add(int fr,int to,ll w)
{
e[cnt].fr=fr;
e[cnt].to=to;
e[cnt].w=w;
e[cnt].nxt=head[fr];
head[fr]=cnt++;
}
int fin(int x)
{
if(x==pre[x])
return x;
return pre[x]=fin(pre[x]);
}
int n,m;
void Kruskal()
{
for(int i=1;i<=n*m;++i)
pre[i]=i;
sort(e,e+cnt,cmp);
ll ans=0;
for(int i=0;i<cnt;++i){
int u=fin(e[i].fr);
int v=fin(e[i].to);
if(u!=v){
ans+=e[i].w;
//e[i].vis=1;
//e[].vis=1;
ee[e[i].fr].push_back(e[i].to);
ee[e[i].to].push_back(e[i].fr);
pre[u]=v;
}
}
//printf("%d\n",ans);
}
int main()
{
ll a,b;
char c,d;
scanf("%d%d",&n,&m);
{
cnt=0;
memset(data,0,sizeof(data));
memset(fa,0,sizeof(fa));
memset(head,-1,sizeof(head));
for(int i=1;i<=n*m;i++)
{
getchar();
scanf("%c%lld %c%lld",&c,&a,&d,&b);
if(c=='D') add(i,i+m,-a);
if(d=='R') add(i,i+1,-b);
}
Kruskal();
}
dfs(1);
int q;
scanf("%d",&q);
int x1,y1,x2,y2;
for(int i=0;i<q;i++)
{
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
int num1=(x1-1)*m+y1;
int num2=(x2-1)*m+y2;
printf("%d\n",LCA(num1,num2));
}
return 0;
}