zoukankan      html  css  js  c++  java
  • loj2392. 「JOISC 2017 Day 1」烟花棒

    题意

    题目说的很清楚了亚。。把题面再复制一遍吧。

    (N)人站在一条数轴上。他们人手一个烟花,每人手中的烟花都恰好能燃烧(T)秒。每个烟花只能被点燃一次。
    (1)号站在原点,(i)号((1 leq i leq N))到(1)号的距离为(X_i)。保证(X_1 = 0)(X_1, X_2, ..., X_N)单调递增(可能有人位置重叠)。
    开始时,(K)号的烟花刚开始燃烧,其他人的烟花均未点燃。他们的点火工具坏了,只能用燃着的烟花将未点燃的烟花点燃。当两人位置重叠且其中一人手中的烟花燃着时,另一人手中的烟花就可以被点燃。忽略点火所需时间。
    求至少需要以多快的速度跑,才能点燃所有人的烟花(此时可能有些人的烟花已经熄灭了)。速度必须是一个非负整数。

    note:所求的速度为全程中最大速度的最小值。

    题解

    本题满足二分性质,我们可以二分一个所求(v),然后判断是否有合法方案。

    注意到对于一个区间([L, R]),且区间中起初有一个烟花是燃烧的,如果可以点燃区间中所有烟花,其中一种方案一定满足,所有人的相对位置不发生改变。因此,在这种方案中,只要满足(x_L + vT(R - L) geq x_R - vT(R - L))即可。而同样,如果不满足(x_L + vT(R - L) geq x_R - vT(R - L))这个条件,那么显然整个区间是不能被全部点燃的(以(v)的速度)。

    观察上面的柿子,移项,得

    [x_L - 2vTL geq x_R - 2vTR ]

    则令(a_i = x_i - 2vTi),则问题变为从区间([K, K])开始向外扩展区间,要保证每个时刻扩展出的区间([l, r])要满足(a_l geq a_r)。当且仅当能扩展到区间([1, n]),所有烟花可以被点燃。

    观察一个性质:对于可扩展区间([l, r]),对于(i < l),如果满足:

    1.(a_i geq a_l)

    2.(forall j in [i, l])(a_j geq a_r)

    则可知区间([i, r])也是可以扩展的。

    就这样,我们可以左右不断做这个操作,每次固定合法区间左(右)端点,向外移动右(左)端点。

    如果某次向左向右都无法扩展了,且是因为(a_i geq a_l wedge exists j in [i, l], a_j < a_r)这样的原因,那么说明无法点燃整个大区间([1, n])的烟花。

    否则,我们会得到一个极大的区间([ml, mr])(这个区间不能通过上述的方式扩展,且是因为( exists i < l, a_i geq a_l))。

    这时,我们只能换一种方式来判断——从整个大区间([1, n])向内缩。

    此时,对于可缩区间([l, r]),对于(l < i leq ml),如果满足:

    1.(a_i geq a_l)

    2.(forall j in [l, i])(a_j geq a_r)

    区间([i, r])也可以缩得。

    直到区间([ml, mr])如果可以缩得,那么就说明可以点燃所有烟花。

    证明?其实这是一个逆向过程,把过程逆一下就挺显然的了。(可以画个折线图试试)

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    int n, K, T, x[N];
    ll a[N];
    
    bool check (int v) {
    	for (int i = 1; i <= n; ++i) {
    		a[i] = 0ll + x[i] - 2ll * T * v * i;
    	}
    	if (a[1] < a[n]) {
    		return 0;
    	}
    	int ql = K, qr = K, l, r;
    	for (int i = K - 1; i; --i) {
    		if (a[i] >= a[ql]) {
    			ql = i;
    		}
    	}
    	for (int i = K + 1; i <= n; ++i) {
    		if (a[i] <= a[qr]) {
    			qr = i;
    		}
    	}
    	for (l = r = K; l != ql || r != qr; ) {
    		int ok = 0, L = l, R = r;
    		for ( ; L > ql && a[L - 1] >= a[r]; ) {
    			if (a[--L] >= a[l]) {
    				break;
    			}
    		}
    		if (L < l && a[L] >= a[l]) {
    			ok = 1, l = L;
    		}
    		for ( ; R < qr && a[R + 1] <= a[l]; ) {
    			if (a[++R] <= a[r]) {
    				break;
    			}
    		}
    		if (R > r && a[R] <= a[r]) {
    			ok = 1, r = R;
    		}
    		if (!ok) {
    			return 0;
    		}
    	}
    	for (l = 1, r = n; l != ql || r != qr; ) {
    		int ok = 0, L = l, R = r;
    		for ( ; L < ql && a[L + 1] >= a[r]; ) {
    			if (a[++L] >= a[l]) {
    				break;
    			}
    		}
    		if (L > l && a[L] >= a[l]) {
    			ok = 1, l = L;
    		}
    		for ( ; R > qr && a[R - 1] <= a[l]; ) {
    			if (a[--R] <= a[r]) {
    				break;
    			}
    		}
    		if (R < r && a[R] <= a[r]) {
    			ok = 1, r = R;
    		}
    		if (!ok) {
    			return 0;
    		}
    	}
    	return 1;
    }
    int main () {
    	scanf("%d%d%d", &n, &K, &T);
    	for (int i = 1; i <= n; ++i) {
    		scanf("%d", &x[i]);
    	}
    	int l = 0, r = 1e9, mid, ans = r;
    	while (l <= r) {
    		mid = (l + r) >> 1;
    		if (check(mid)) {
    			ans = mid;
    			r = mid - 1;
    		} else {
    			l = mid + 1;
    		}
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    
  • 相关阅读:
    单例 全局变量
    平安亲人 测试数据
    UIButton
    apple id
    背景图的按钮
    大头针飘移问题
    图文并茂Windows系统使用XAMPP搭建本地mysql数据库导入数据库并使用node.js访问数据库
    JS原生上传文件,读取文件格式,控制文件只可以上传某些格式,并使用fileReader转换格式
    今天学到的新知识使用localtunnel实现内网穿透,感觉很神奇哇~~
    在Mac OS上将Node.js连接到XAMPP MySQL服务器一直报错error connecting: Error: connect ECONNREFUSED
  • 原文地址:https://www.cnblogs.com/psimonw/p/11203634.html
Copyright © 2011-2022 走看看