zoukankan      html  css  js  c++  java
  • @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 了。

  • 相关阅读:
    maven问题
    用例图中三种关系详解(转)
    UML系列图--用例图
    Visio画UML用例图没有include关系的解决方法
    Linux中环境变量文件及配置
    如何开启ubuntu的SSH服务(不要和openssl搞混淆了)
    linux下的gedit命令使用方法与技巧
    ubuntu安装mysql5.7
    通过 HTTP 头进行 SQL 注入
    Redis各种数据结构内存占用测试
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11072195.html
Copyright © 2011-2022 走看看