Gym - 101173H Hangar Hurdles (kruskal重构树/最小生成树+LCA)

题目大意:给出一个n*n的矩阵,有一些点是障碍,给出Q组询问,每组询问求两点间能通过的最大正方形宽度。

首先需要求出以每个点(i,j)为中心的最大正方形宽度mxl[i][j],可以用二维前缀和+二分或者BFS求。

然后每相邻的两个点建一条权值为min(mxl[i][j],mxl[i‘][j‘])的边,求出整个图的最小生成树(注意边权要从大到小排序,实际上求出的是边权的“最大生成树”)或者kruskal重构树,对于每组询问(x1,y1),(x2,y2),答案为最小生成树上两点间路径的最小边权,或者kruskal重构树上两点LCA的权值。

如果建的是最小生成树,需要启发式合并(或者路径压缩,新开一个fa数组记录合并后的),如果建的是kruskal重构树,则需要弄个树剖或者倍增,加速求LCA的过程。

版本一(kruskal重构树+二维前缀和):

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1000+10;
 4 char s[N][N];
 5 int n,a[N][N],mxl[N][N],m,Fa[N*N*2],Tot,Q,hd[N*N*2],ne,C[N*N*2];
 6 int fa[N*N*2],son[N*N*2],siz[N*N*2],dep[N*N*2],top[N*N*2];
 7 struct E {int v,nxt;} e[N*N*2];
 8 void addedge(int u,int v) {e[ne]= {v,hd[u]},hd[u]=ne++;}
 9 void dfs1(int u,int f,int d) {
10     fa[u]=f,son[u]=0,siz[u]=1,dep[u]=d;
11     for(int i=hd[u]; ~i; i=e[i].nxt) {
12         int v=e[i].v;
13         if(v==fa[u])continue;
14         dfs1(v,u,d+1),siz[u]+=siz[v];
15         if(siz[v]>siz[son[u]])son[u]=v;
16     }
17 }
18 void dfs2(int u,int tp) {
19     top[u]=tp;
20     if(son[u])dfs2(son[u],top[u]);
21     for(int i=hd[u]; ~i; i=e[i].nxt) {
22         int v=e[i].v;
23         if(v==fa[u]||v==son[u])continue;
24         dfs2(v,v);
25     }
26 }
27 int lca(int u,int v) {
28     for(; top[u]!=top[v]; u=fa[top[u]])
29         if(dep[top[u]]<dep[top[v]])swap(u,v);
30     if(dep[u]<dep[v])swap(u,v);
31     return v;
32 }
33 int sum(int x1,int y1,int x2,int y2) {return a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];}
34 int ok(int i,int j,int x) {return !sum(i-x,j-x,i+x,j+x);}
35 int bi(int i,int j,int l,int r) {
36     int ret;
37     while(l<=r) {
38         int mid=(l+r)>>1;
39         if(ok(i,j,mid))l=mid+1,ret=mid;
40         else r=mid-1;
41     }
42     return ret;
43 }
44 struct E2 {
45     int x1,y1,x2,y2,c;
46     bool operator<(const E2& b)const {return c>b.c;}
47 } e2[N*N*2];
48 int fd(int x) {return Fa[x]?Fa[x]=fd(Fa[x]):x;}
49 int id(int x,int y) {return (x-1)*n+(y-1)+1;}
50 void kruskal() {
51     sort(e2,e2+m);
52     Tot=n*n;
53     memset(hd,-1,sizeof hd),ne=0;
54     for(int i=1; i<=n; ++i)
55         for(int j=1; j<=n; ++j)C[id(i,j)]=mxl[i][j];
56     for(int i=0; i<m; ++i) {
57         int x1=e2[i].x1,y1=e2[i].y1,x2=e2[i].x2,y2=e2[i].y2,c=e2[i].c;
58         int fx=fd(id(x1,y1)),fy=fd(id(x2,y2));
59         if(fx==fy)continue;
60         int w=++Tot;
61         C[w]=c;
62         Fa[fx]=Fa[fy]=w;
63         addedge(w,fx),addedge(w,fy);
64     }
65 }
66 int main() {
67     scanf("%d",&n);
68     for(int i=1; i<=n; ++i)scanf("%s",s[i]+1);
69     for(int i=1; i<=n; ++i)for(int j=1; j<=n; ++j)a[i][j]=s[i][j]==‘#‘;
70     for(int i=1; i<=n; ++i)
71         for(int j=1; j<=n; ++j)
72             a[i][j]=a[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1];
73     for(int i=1; i<=n; ++i)
74         for(int j=1; j<=n; ++j)
75             mxl[i][j]=s[i][j]==‘#‘?0:bi(i,j,0,min(min(i-1,n-i),min(j-1,n-j)))*2+1;
76     for(int i=1; i<=n; ++i)
77         for(int j=1; j<=n; ++j) {
78             if(i<n)e2[m++]= {i,j,i+1,j,min(mxl[i][j],mxl[i+1][j])};
79             if(j<n)e2[m++]= {i,j,i,j+1,min(mxl[i][j],mxl[i][j+1])};
80         }
81     kruskal();
82     dfs1(Tot,0,1),dfs2(Tot,Tot);
83     scanf("%d",&Q);
84     while(Q--) {
85         int x1,y1,x2,y2;
86         scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
87         printf("%d\n",C[lca(id(x1,y1),id(x2,y2))]);
88     }
89 }

版本二(最小生成树+BFS):

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int N = 1000+10;
 4 char s[N][N];
 5 int n,a[N][N],mxl[N][N],m,fa[N*N],Q,C[N*N],siz[N*N],dep[N*N];
 6 struct E {
 7     int x1,y1,x2,y2,c;
 8     bool operator<(const E& b)const {return c>b.c;}
 9 } e[N*N*2];
10 struct D {int x,y;};
11 queue<D> q;
12 void upd(int x,int y,int c) {if(!~mxl[x][y])mxl[x][y]=c,q.push({x,y});}
13 void bfs() {
14     while(q.size())q.pop();
15     memset(mxl,-1,sizeof mxl);
16     for(int i=0; i<=n+1; ++i)
17         for(int j=0; j<=n+1; ++j)if(i<1||i>n||j<1||j>n||s[i][j]==‘#‘)upd(i,j,0);
18     while(q.size()) {
19         int x=q.front().x,y=q.front().y;
20         q.pop();
21         for(int x2=x-1; x2<=x+1; ++x2)
22             for(int y2=y-1; y2<=y+1; ++y2) {
23                 if(x2<1||x2>n||y2<1||y2>n||~mxl[x2][y2])continue;
24                 upd(x2,y2,mxl[x][y]+1);
25             }
26     }
27     for(int i=1; i<=n; ++i)
28         for(int j=1; j<=n; ++j)if(s[i][j]==‘.‘)mxl[i][j]=mxl[i][j]*2-1;
29 }
30 int fd(int x) {return fa[x]?fd(fa[x]):x;}
31 int id(int x,int y) {return (x-1)*n+(y-1)+1;}
32 int dfs(int u) {if(!fa[u])return 0; if(dep[u])return dep[u]; return dep[u]=dfs(fa[u])+1;}
33 void kruskal() {
34     sort(e,e+m);
35     for(int i=1; i<=n; ++i)
36         for(int j=1; j<=n; ++j)siz[id(i,j)]=1;
37     for(int i=0; i<m; ++i) {
38         int x1=e[i].x1,y1=e[i].y1,x2=e[i].x2,y2=e[i].y2,c=e[i].c;
39         int fx=fd(id(x1,y1)),fy=fd(id(x2,y2));
40         if(fx==fy)continue;
41         if(siz[fx]>siz[fy])swap(fx,fy);
42         fa[fx]=fy,C[fx]=c,siz[fy]+=siz[fx];
43     }
44     for(int i=1; i<=n; ++i)
45         for(int j=1; j<=n; ++j)dfs(id(i,j));
46 }
47 int qry(int x1,int y1,int x2,int y2) {
48     int ret=min(mxl[x1][y1],mxl[x2][y2]);
49     int u=id(x1,y1),v=id(x2,y2);
50     while(u!=v) {
51         if(dep[u]<dep[v])swap(u,v);
52         ret=min(ret,C[u]);
53         u=fa[u];
54     }
55     return ret;
56 }
57 int main() {
58     scanf("%d",&n);
59     for(int i=1; i<=n; ++i)scanf("%s",s[i]+1);
60     bfs();
61     for(int i=1; i<=n; ++i)
62         for(int j=1; j<=n; ++j) {
63             if(i<n)e[m++]= {i,j,i+1,j,min(mxl[i][j],mxl[i+1][j])};
64             if(j<n)e[m++]= {i,j,i,j+1,min(mxl[i][j],mxl[i][j+1])};
65         }
66     kruskal();
67     scanf("%d",&Q);
68     while(Q--) {
69         int x1,y1,x2,y2;
70         scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
71         printf("%d\n",qry(x1,y1,x2,y2));
72     }
73 }

原文地址:https://www.cnblogs.com/asdfsag/p/11622902.html

时间: 2024-09-29 04:43:37

Gym - 101173H Hangar Hurdles (kruskal重构树/最小生成树+LCA)的相关文章

【BZOJ 3732】 Network Kruskal重构树+倍增LCA

Kruskal重构树裸题, Sunshine互测的A题就是Kruskal重构树,我通过互测了解到了这个神奇的东西... 理解起来应该没什么难度吧,但是我的Peaks连WA,,, 省选估计要滚粗了TwT #include<cstdio> #include<cstring> #include<algorithm> #define for1(i,a,n) for(int i=(a);i<=(n);i++) #define for2(i,a,n) for(int i=(a

BZOJ 3551 ONTAK2010 Peaks加强版 Kruskal重构树+可持久化线段树

题目大意:同3545 强制在线 3545题解传送门:http://blog.csdn.net/popoqqq/article/details/40660953 强制在线没法排序 启发式合并也就用不了了 Kruskal重构树是个挺好玩的东西 可以拿来处理一些最小生成树的边权最值问题 这里我们Kruskal连边时并不直接连边 而是新建一个节点ext 将两个点所在子树都连到ext的儿子上 比如说样例的树就建成了这样 图中红色的是原图的边权,黑色的是原图上的点 这样生成的树有一些十分优美的性质: 1.二

算法学习——kruskal重构树

kruskal重构树是一个比较冷门的数据结构. 其实可以看做一种最小生成树的表现形式. 在普通的kruskal中,如果一条边连接了在2个不同集合中的点的话,我们将合并这2个点所在集合. 而在kruskal重构树中,如果一条边连接了在2个不同集合中的点,我们将新建一个节点出来,并用这个新节点作为一个中转连接这2个集合. 如图就是一棵kruskal重构树,方点表示新建出的节点,圆点是原图中的点,方点点权即边权. 这样建出的树会有一些美妙的性质,例如往上走点权是递增的,原图中的每个点都是叶子节点等.

[IOI2018]werewolf狼人——kruskal重构树+可持久化线段树

题目链接: IOI2018werewolf 题目中编号都是从0开始,太不舒服了,我们按编号从1开始讲QAQ. 题目大意就是询问每次从一个点开始走只能走编号在[l,n]中的点,在任意点变成狼,之后只能走[0,r]中的点,是否能到达另一个点. 后一部分其实就是找有哪些点只走[0,r]中的点能到达终点,那么反过来看,就是终点只走[0,r]中的点能到达哪些点. 那么只要起点能到达的点和终点能到达的点中有交集就有解. 因为起点只能走一些编号较大的点,那么我们求出原图的最大生成树,建出kruskal重构树,

【BZOJ 3551】[ONTAK2010] Peaks加强版 Kruskal重构树+树上倍增+主席树

这题真刺激...... I.关于Kruskal重构树,我只能开门了,不过补充一下那玩意还是一棵满二叉树.(看一下内容之前请先进门坐一坐) II.原来只是用树上倍增求Lca,但其实树上倍增是一种方法,Lca只是他的一种应用,他可以搞各种树上问题,树上倍增一般都会用到f数组. |||.我们跑出来dfs序就能在他的上面进行主席树了. IV.别忘了离散. V.他可能不连通,我一开始想到了,但是我觉得出题人可能会是好(S)人(B),但是...... #include <cstdio> #include

Kruskal重构树

不支持时间旅行的可持久化并查集 给定 n 个点, 以及 m 次操作, 操作有两种: ① 将点 x 与点 y 进行连边; ② 询问在前 t 次操作操作中, x 与 y 是否连通. n <= 100000, 强制在线. 核心模型 n 个点, m 条带权边的无向图. 多次询问点 x 和点 y 在边权不超过 w 的边的作用下的连通性信息(例如, 是否连通). 强制在线. 对于不支持时间旅行的并查集问题, 将时间这一维给量化之后, 可以等价地看成这个核心模型. 对于操作①, 我们相当于连边 (x, y)

BZOJ 3732 Network Kruskal重构树

题目大意:给定一个n个点m条边的无向连通图,k次询问两点之间所有路径中最长边的最小值 Kruskal+倍增LCA做法见http://blog.csdn.net/popoqqq/article/details/39755703 LCT做法见http://blog.csdn.net/popoqqq/article/details/39929277 Kruskal重构树真是强大--一不小心手滑就RANK1啥的-- 每加入一条边时,我们并不链接这条边的两端点,而是把这条边两端点所在并查集的根连接起来,而

[IOI2018] werewolf 狼人 kruskal重构树,主席树

[IOI2018] werewolf 狼人 LG传送门 kruskal重构树好题. 日常安利博客文章 这题需要搞两棵重构树出来,这两棵重构树和我们平时见过的重构树有点不同(据说叫做点权重构树?),根据经过我们简化的建树方法,这两棵树不再是二叉树,但是仍具有kruskal重构树的优秀性质,建议结合后面的描述理解. 看这题需要首先我们从\(S\)走到\(T\)转化为分别从\(S\)和\(T\)出发寻找能共同到达的点,需要快速求出从某个点出发经过点权不大(小)于\(r\)(\(l\))的点,考虑kru

【BZOJ 3545】【ONTAK 2010】Peaks &amp; 【BZOJ 3551】【ONTAK 2010】Peaks加强版 Kruskal重构树

sunshine的A题我竟然调了一周!!! 把循环dfs改成一个dfs就可以,,,我也不知道为什么这样就不会RE,但它却是A了,,, 这周我一直在调这个题,总结一下智障错误: 1.倍增的范围设成了n而不是n*2-1,,, 2.重构树的顶点是n*2-1,而我一开始设成了n,,, 3.define里的for3和for4的i--打成i++,,,,,,,,,,,, 4.dfs爆栈了,找CA爷问的编译命令里手动扩栈,真是愚蠢的问题,,,, 比赛时绝不会有太多时间,在这么犯逗就得滚粗了QAQ 3545: #