• @noi.ac



    @description@

    小Q计划在自己的新家中购置一台圆形的扫地机器人。小Q的家中有一个宽度为 m 的走廊,走廊很长,如果将这个走廊的俯视图画在平面直角坐标系上的话,那么走廊的两堵墙可以分别看作直线 y=0 和直线 y=m,两堵墙之间的部分代表走廊。

    小Q会按照顺序依次在走廊中安置 n 个家具。第 i 个家具的位置为 (xi,yi),宽度可以忽略不计,同一个位置可能会有多个家具。

    在商店中,扫地机器人的半径只能是整数。请找到最大可能的整数半径 R,使得以 R 为半径的扫地机器人可以从走廊的最左侧到达最右侧,扫地机器人不可以穿过家具或者墙壁,但是允许接触它们。

    请写一个程序,帮助小 Q 在每次安置下新的家具后,都能计算出这个条件下允许通过的扫地机器人的最大可能半径。

    input
    第一行包含两个正整数 n, m,分别表示家具的数量和走廊的宽度。

    接下来 n 行,每行两个正整数 xi, yi,表示第 i 个被安置下的家具的位置。

    output
    输出 n 行,每行输出一个整数,第 i 行输出在安置下前 i 个家具后,扫地机器人的半径的最大可能值。

    sample input
    5 6
    1 2
    3 2
    2 1
    1 3
    4 5
    sample output
    2
    2
    2
    1
    1

    对于 100% 的数据:1≤xi≤10^9,1≤yi<m≤10^9,n≤2500。

    @solution@

    不妨看看给定机器人半径为 r0 的情况下会发生什么。

    我们可以以障碍为圆心,画出一个半径为 r0 的禁行区域(即:机器人的圆心不能经过这个区域)。
    同时也可以以两面墙画出相应的禁行区域。
    此时如果禁行区域将两面墙连接在一起,该半径 r0 不合法。

    稍微转换一下:
    如果半径 x 是使得障碍/墙 a, b 所对应的禁行区域连接(即有交集)的最小整数半径,我们就在 a, b 之间连一条边权为 x 的边。
    当半径为 r0 的时候,如果存在一条两面墙之间的路径,使得路径上的每一条边的边权 <= r0,则 r0 不合法。
    等价于路径上的最大边权 <= r0。

    题目要求的是最大合法的整数半径 R,但我们可以将问题做一个简单的转换:找到最小不合法的整数半径 R'。
    因为是整数,所以可以得到 R = R' - 1。

    问题最终可以转换为:找到两墙之间的一条路径,使这条路径上的最大值最小。
    这是一个典型的最小生成树应用。

    怎么动态维护最小生成树呢?
    一开始我原本想的是用 lct 来搞,看了题解才发现:

    woc 它只需要求 O(n) 次最小生成树,所以没必要每个时刻的最小生成树都求解出来。
    于是:每次加入一个新的障碍,增加 O(n) 条边,与上个时刻的最小生成树一起(也是 O(n) 条边)求解最小生成树。
    跑 kruskal 即可。所以总的复杂度是优秀的 O(n^2log n)。

    @accepted code@

    #include<cmath>
    #include<vector>
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int MAXN = 2500 + 10;
    const ll INF = (1LL<<60);
    struct edge{
    	int u, v; ll d;
    	edge(int _u=0, int _v=0, ll _d=0):u(_u), v(_v), d(_d){}
    	friend bool operator < (edge a, edge b) {return a.d < b.d;}
    }edges[2*MAXN];
    int fa[MAXN];
    int find(int x) {
    	return fa[x] = (fa[x] == x) ? x : find(fa[x]);
    }
    ll m, x[MAXN], y[MAXN]; int n;
    ll dist(int i, int j) {
    	return ll(sqrt((x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j])));
    }
    vector<pair<int, ll> >G[MAXN];
    void addedge(int u, int v, ll d) {
    	G[u].push_back(make_pair(v, d));
    	G[v].push_back(make_pair(u, d));
    }
    void dfs(int x, int f, ll d) {
    	if( x == n + 2 ) {
    		printf("%lld
    ", d - 1);
    		return ;
    	}
    	for(int i=0;i<G[x].size();i++)
    		if( G[x][i].first != f )
    			dfs(G[x][i].first, x, max(d, G[x][i].second));
    }
    int main() {
    	scanf("%d%lld", &n, &m);
    	edges[1] = edge(n + 1, n + 2, m/2 + 1);
    	for(int i=1;i<=n;i++) {
    		scanf("%lld%lld", &x[i], &y[i]);
    		for(int j=1;j<i;j++)
    			edges[i+j] = edge(i, j, dist(i, j)/2 + 1);
    		edges[2*i] = edge(i, n + 1, y[i]/2 + 1);
    		edges[2*i+1] = edge(i, n + 2, (m - y[i])/2 + 1);
    		sort(edges + 1, edges + 2*i + 2);
    		for(int j=1;j<=n+2;j++)
    			fa[j] = j, G[j].clear();
    		int cnt = 0;
    		for(int j=1;j<=2*i+1;j++) {
    			if( find(edges[j].u) != find(edges[j].v) ) {
    				fa[find(edges[j].u)] = find(edges[j].v);
    				edges[++cnt] = edges[j];
    			}
    		}
    		for(int j=1;j<i+2;j++)
    			addedge(edges[j].u, edges[j].v, edges[j].d);
    		dfs(n + 1, -1, -INF);
    	}
    }
    

    @details@

    康复计划 - 1。

    还好。没有什么大的问题。我能记得最小生成树有这个经典应用感觉已经很奇迹了。

    只是看题解之前差点就要写 lct 了。

  • 相关阅读:
    Haskell Interactive Development in Emacs
    Access Java API in Groovy Script
    手工设置Eclipse文本编辑器的配色
    Color Theme of Emacs
    Gnucash的投资记录
    Special Forms and Syntax Sugars in Clojure
    Use w3m as Web Browser
    SSE指令集加速之 I420转BGR24
    【图像处理】 增加程序速度的方法
    TBB 入门笔记
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11072195.html
Copyright © 2020-2023  润新知