• 模拟赛 18 施工 小模拟+神奇维护



    这道题我是看的 DeepinC 的题解, 思路和大多数人不一样, 但是他写的不是很明白, 我来详细地描述一下

    读入后扫一遍, 对于每一个平底坑, 预处理出来, 然后不断维护每一个坑的填一单位高度的代价 和代价增长率, 如果填坑更优, 那么就填。

    注意, 因为代价增长是一个二次函数, 所以可以直接用公式(-dfrac {b} {2a})计算即可。

    其他细节可以看代码

    #include <bits/stdc++.h>
    #define gc() (p1 == p2 ? (p2 = buf + fread(p1 = buf, 1, 1 << 20, stdin), p1 == p2 ? EOF : *p1++) : *p1++)
    #define read() ({ register int x = 0, f = 1; register char c = gc(); while(c < '0' || c > '9') { if (c == '-') f = -1; c = gc();} while(c >= '0' && c <= '9') x = x * 10 + (c & 15), c = gc(); f * x; })
    char buf[1 << 20], *p1, *p2;
    using namespace std;
    int n, l[2000005], r[2000005], tot, rb[1000005], lb[1000005], c;
    long long h[1000005], a[2000005], w[2000005], ans;
    int main() {
    	freopen("construct.in", "r", stdin), freopen("construct.out", "w", stdout);
    	n = read(), c = read();
    	for (register int i = 1; i <= n; i++) h[i] = read();
    	for (register int i = 2; i <= n; i++) ans += abs(h[i] - h[i - 1]) * c; // 先计算一遍 ans
    	h[0] = h[n + 1] = INT_MAX;
    	for (register int i = 1, j; (j = i) <= n; i = j + 1) {
    		while (h[j + 1] == h[i]) j++; // 高度相同时, 不断扩展
    		if (h[i - 1] > h[i] && h[i] < h[j + 1]) {
    			l[++tot] = i; 		// 左端点
    			r[tot] = j;  		// 右端点
    			w[tot] = j - i + 1;	// 当前高度下, 每建一单位高度所需代价
    			a[tot] = w[tot] * 2;// 高度每增加1, w[i] 所增加的值, 详细解释在第 43 行
    		}
    	}
    	for (register int i = 1; i <= tot; i++) {
    		// 	e 为增长一高度 不美观度减少的值, t为增加的高度
    		long long e = 0, t;
    		if (l[i] != 1) e += c;
    		if (r[i] != n) e += c;
    		// 	如果增长一高度, 减少的不美观度少于代价, 处理下一个坑
    		if (e < w[i]) continue;
    		// 	计算增加的高度, -(2 * a / b) 和 深度 取最小值
    		t = min((e - w[i]) / a[i] + 1, min(h[r[i] + 1], h[l[i] - 1]) - h[l[i]]);
    		h[l[i]] += t;
    		if (l[i] != r[i]) h[r[i]] += t;
    		// 	e * t 为减少的不美观值
    		ans -= e * t;
    		// 	这里是一个等差数列求和公式, 首项为 w[i], 公差为 a[i], 共 t - 1 项
    		// 	w[i] 不同高度下, 增加一高度的代价, w[i] 求和即为增加 t 的代价
    		ans += (w[i] + (w[i] + (t - 1) * a[i])) * t / 2;
    		//	更新 w[i]
    		w[i] += a[i] * t;
    		//	高度增加后向左扩展
    		while (h[l[i]] == h[l[i] - 1] && !rb[l[i] - 1]) {
    			// 	因为增加了一个之前没有增加过高度的点, 所以 w[i]++ (0 -> 1), 而 a[i] += 2
    			// 	代价增长如下:
    			//	0 -> 1 -> 4 -> 9 -> 16 -> 25
    			// 	则 w[i] 增长如下
    			//	   1 -> 3 -> 5 -> 7 -> 9
    			//  则 a[i] 为
    			// 	 	  2 -> 2 -> 2 -> 2
    			// 所以每增加一个新点, a[i] += 2
    			l[i]--, w[i]++, a[i] += 2;
    		}
    		// 	扩展到另一个坑, 合并
    		if (h[l[i]] == h[l[i] - 1])
    			w[i] += w[rb[l[i] - 1]], a[i] += a[rb[l[i] - 1]], l[i] = l[rb[l[i] - 1]];
    		//	向右同理
    		while (h[r[i]] == h[r[i] + 1] && !lb[r[i] + 1])
    			r[i]++, w[i]++, a[i] += 2;
    		if (h[r[i]] == h[r[i] + 1])
    			w[i] += w[lb[r[i] + 1]], a[i] += a[lb[r[i] + 1]], r[i] = r[lb[r[i] + 1]];
    		lb[l[i]] = rb[r[i]] = i; //	打上标记
    		// 如果 还能再增长, 就再计算一遍
    		i -= h[l[i] - 1] > h[l[i]] && h[r[i]] < h[r[i] + 1];
    	}
    	printf("%lld
    ", ans);
    }
    
  • 相关阅读:
    C#获取配置文件中的文件数据
    wpf MVVMLight的DataGrid绑定数据
    扫码支付自动跳转,可以使用第三方网站实现扫码二维码付款然后跳转到想要的页面展示想要内容或者是解压码或者是某个资源的下载页呢 具体步骤(我以你上传一个压缩包到某种网盘或者可以下载的地址等让人付费解压为例):
    oracle数据库如何创建用户以及分配权限
    ORA-12547: TNS: 丢失连接
    springmvc中applicationapplicationContext头部代码
    No mapping found for HTTP request with URI
    在Navicat新建用户
    myeclipse 项目引入 com.sun.image.codec.jpeg 的api报错解决方法
    java.lang.NullPointerException at org.apache.jsp.**_jsp.jspInit(**_jsp.java)tomcat启动异常解决方法
  • 原文地址:https://www.cnblogs.com/youxam/p/construct.html
Copyright © 2020-2023  润新知