0%

[CF1920F2] Smooth Sailing (Hard Version)

哎,人类智慧题。

从来没有见过这样的题,写篇题解纪念之。

题目链接。

题意

现在有一个大小为 的网格,格子有三种,海水、火山与岛屿。

定义一个格子的权值,为到所有火山的曼哈顿距离的最小值。

现在有 次询问,每一次给定格子 ,你现在需要从 这个格子出发,每一次走到相邻的非岛屿的四连通格子内,包围整个岛屿,并回到格子 (可以经过相同格子)。

对于每一组询问,你的路径的权值,是所有经过格子的权值的最小值,你需要输出路径权值的最大值。

其中,包围的定义是,不存在一条从岛屿出发的八连通的路径,可以不经过你走到的格子,到达网格边界。

。

。

题解

既然要求路径的权值,不妨先把每一个格子的权值求出来。

这一步是简单的,可以使用多源 bfs 在 的复杂度内求出。

考虑什么样的路径是合法的。

考虑从岛屿的边界为起点,引一条只经过格子边界(不经过岛屿边界),且终点在网格边界的简单折线(例如下图黑色折线)。

可以发现,任意一个可能作为答案的方案合法,当且仅当穿过了这条线奇数次(例如下图白线)。

一个粗略的证明:

以上图黑色折线为例,可以发现,折线的一端在岛屿边界,而另一端在网格边界,这样整张图就从一个环形的结构,被黑线分成了一个链式的结构。

再来考虑任何一条合法路径(例如白线),我们先给它选一个方向(顺时针或逆时针,这里以顺时针举例)。

我们再给黑色折线的每一侧定一个方向,例如上下。

这个时候来观察白线与黑色折线的所有交点。

可以发现,白线被分成了若干段(废话,白线若没有经过黑线一定不合法)。

由于我们给白线定了方向,又给黑线定了上下侧的区别。

那么每一段白线都可以划分为以下四种类型之一:上侧到上侧、上侧到下侧、下侧到上侧、下侧到下侧。

例如,白线中最长的一段就是从下侧到上侧,特殊的,这个例子中整个白线中并没有出现上侧到下侧。

回到证明中,假设白线被黑线分了 段,那么就会有 个交点,上侧与下侧在所有线段类型中便会各出现 次。

这时,如果 是奇数,那就必然会出现上侧到下侧和下侧到上侧中的至少一种。

这时候证明就很简单了,因为白线连成了环状,所以只要出现上述两种情况中的一种,岛屿就一定被“包围”。

现在我们就证明了,穿过黑线奇数次的方案,一定是合法的。

那么穿过黑线偶数次的方案呢?它们一定不合法吗?

答案是,这些方案可能合法,但没有必要管他们。

来看这两种情况,红线都穿过黑线偶数次,但它们都是合法方案(加粗点为起点)。

容易发现,如果这种情况合法,那么上侧到下侧、下侧到上侧出现次数和必然 ,那么整个路径就必然有自相交的部分。

既然有相交的部分,我们就可以化简。

只保留绿色部分,答案一定不劣,并且经过格子数量变少。

一直化简下去,红线一定能变为只穿过黑线奇数次,并且答案不劣于原来的方案。

所以经过黑线奇数次的答案要么不合法,要么没必要统计。

那么如何取黑线呢?可以参照第一张图,从岛屿边缘直接引一条普通线段即可(那条黄线)。

现在我们知道了什么情况是合法的,考虑如何计算答案。

那么现在的问题就是,对于每一组询问的点 ,我们需要找一条路径,使得它穿过了黑线奇数次,并回到 ,需要最大化这条路径的权值。

首先想暴力怎么做(首先设 表示格子 的权值)。

我们可以令 分别表示,现在在点 ,经过了黑线奇数次还是偶数次,最大权值是多少。

那么答案就是 。

我们可以令初始情况为 ,然后跑一个最短路即可。

这样单组的复杂度是 的,可以通过 F1。

这个时候我们可以发现一些有趣的事情,例如对于所有询问来说,我们建的图都长得一样。

我们还可以改变一下求的东西,不难发现 表示的其实是,找出一条起点为 且终点为 的路径,使它路径上点的权值最小值最大。

这不是我们重构树能干的事吗?

所以我们只需要一开始把最大生成树的重构树建出来(一条边 边权的定义是 )。

之后对于所有的询问 ,我们只需要查出点 与 的 LCA 即可。

时间复杂度是 ,其实 完全可以消掉,但是加上也能过。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f3f3f3f3fll
#define debug(x) cerr<<#x<<"="<<x<<endl
using namespace std;
using ll=long long;
using ld=long double;
using pli=pair<ll,int>;
using pi=pair<int,int>;
template<typename A>
using vc=vector<A>;
inline int read()
{
int s=0,w=1;char ch;
while((ch=getchar())>'9'||ch<'0') if(ch=='-') w=-1;
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
int dx[4]={1,-1,0,0};
int dy[4]={0,0,1,-1};
struct node
{
int u,v;
int w;
node(int u=0,int v=0,int w=0):u(u),v(v),w(w){}
}e[1200001];
int fa[1200001][22];
int dep[1200001];
int w[1200001];
int f[1200001];
int dis[300005];
char s[300005];
int rx,ry;
int n,m,q;
int cntm;
int cnt;
inline void bfs()
{
queue<int>que;
memset(dis,0x3f,sizeof(dis));
for(int i=0;i<n*m;i++) if(s[i]=='v') dis[i]=0,que.push(i);
while(!que.empty())
{
int u=que.front();que.pop();
int x=u/m,y=u%m;
for(int i=0;i<4;i++)
{
int vx=x+dx[i],vy=y+dy[i];
if(vx<0||vx>=n||vy<0||vy>=m) continue;
int s=vx*m+vy;
if(dis[s]>dis[u]+1) dis[s]=dis[u]+1,que.push(s);
}
}
}
inline int find(int num)
{
if(f[num]==num) return num;
return f[num]=find(f[num]);
}
inline int lca(int u,int v)
{
if(dep[u]<dep[v]) swap(u,v);
for(int i=20;i>=0;i--) if(dep[u]-(1<<i)>=dep[v]) u=fa[u][i];
if(u==v) return u;
for(int i=20;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
int main()
{
n=read(),m=read(),q=read();
for(int i=0;i<n;i++)
{
scanf("%s",s+i*m);
for(int j=ry;j<=m;j++) if(s[i*m+j]=='#') rx=i,ry=j;
}
bfs();
for(int i=0;i<n;i++) for(int j=0;j<m;j++) if(s[i*m+j]!='#')
{
int v=i*m+j;
if(i&&s[v-m]!='#')
{
if(i==rx&&j>=ry)
{
e[++cntm]=node(v+n*m,v-m,min(dis[v],dis[v-m]));
e[++cntm]=node(v,v-m+n*m,min(dis[v],dis[v-m]));
}
else
{
e[++cntm]=node(v,v-m,min(dis[v],dis[v-m]));
e[++cntm]=node(v+n*m,v-m+n*m,min(dis[v],dis[v-m]));
}
}
if(j&&s[v-1]!='#')
{
e[++cntm]=node(v,v-1,min(dis[v],dis[v-1]));
e[++cntm]=node(v+n*m,v-1+n*m,min(dis[v],dis[v-1]));
}
}
cnt=2*n*m-1;
sort(e+1,e+cntm+1,[](node x,node y){ return x.w>y.w;});
for(int i=0;i<=cnt;i++) f[i]=i;
memset(fa,-1,sizeof(fa));

for(int i=1;i<=cntm;i++)
{
int u=find(e[i].u),v=find(e[i].v);
if(u==v) continue;
cnt++,w[cnt]=e[i].w;
f[u]=f[v]=f[cnt]=cnt;
fa[u][0]=fa[v][0]=cnt;
}
for(int i=cnt;i>=0;i--)
{
for(int j=0;fa[i][j]>0;j++) fa[i][j+1]=fa[fa[i][j]][j];
if(fa[i][0]>0) dep[i]=dep[fa[i][0]]+1;
}
for(int i=1;i<=q;i++)
{
int x=read()-1,y=read()-1;
int v=x*m+y;
printf("%d\n",w[lca(v,v+n*m)]);
}
return 0;
}

感谢观看!