codeforces 892B B. Wrath

https://codeforces.com/problemset/problem/892/B

/*
https://codeforces.com/problemset/problem/892/B

有 n 个有罪的人排成一行,其中第 i 个手持长度为 Li 的爪子。钟声响起,每个人都杀死了他前面的一些人。所有人同时杀死其他人。
也就是说,当且仅当 j < i 且 j ≥ i - Li 时,第 i 个人杀死第 j 个人。

你得到了爪子的长度。您需要找出铃声响起后活着的总人数。

输入
第一行包含一个整数 n(1 ≤ n ≤ 106)--有罪人数。

第二行包含 n 个空格分隔的整数 L1、L2、......、Ln(0 ≤ Li ≤ 109),其中 Li 是第 i 个人爪子的长度。

输出
打印一个整数--铃声响起后活着的总人数。

Examples
InputCopy
4
0 1 0 10
OutputCopy
1
InputCopy
2
0 0
OutputCopy
2
InputCopy
10
1 1 3 0 0 0 2 1 0 3
OutputCopy
3


8
0 0 0 1 0 0 1 2


5
注释
在第一个样本中,最后一个人杀死了他面前的所有人。
*/

//一题多解
//1 差分? O(n)
//2 线段合并,然后二分查找当前元素是否处于一个线段的结束和一个新线段开始的中间   O(nlogn)
//3 官解	 第i名能否存活

差分代码

#include <iostream>
#include <cstring>
 
 
using namespace std;
 
const int N = 1000010;
int a[N];
int pre[N];
int n;
 
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		int l = max(0, i - a[i]); int r = i;
		pre[l]++; pre[r ]--;
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		pre[i] += pre[i - 1];
		if (pre[i] == 0) ans++;
	}
	cout << ans << endl;
 
 
	return 0;
}

线段合并然后检测点是否在被覆盖的区域里

#include <iostream>
#include <algorithm>
 
using namespace std;
 
const int N = 1000010;
int a[N];
int n;
 
struct SEG {
    int l;
    int r;
} segs[N];
 
bool cmp(const SEG& a, const SEG& b) {
    if (a.l < b.l) return true;
    else if (a.l == b.l) return a.r < b.r;
    return false;
}
 
int merges[2 * N];
int idx;
 
int main() {
    cin >> n;
 
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        segs[i].l = max(0, i - a[i]);
        segs[i].r = i-1;
    }
 
    sort(segs + 1, segs + n + 1, cmp);
 
    SEG curr = { -1,-1 };
    
    for (int i = 1; i <= n; i++) {
        if (segs[i].l > segs[i].r) continue;
        if (curr.r == -1) {
            curr = segs[i];
        }
        if (curr.r >= segs[i].l) {
            curr.l = min(curr.l, segs[i].l);
            curr.r = max(curr.r, segs[i].r);
        }
        else {
            merges[++idx] = curr.l;
            merges[++idx] = curr.r;
 
            curr.l = segs[i].l;
            curr.r = segs[i].r;
        }
    }
 
    if (curr.l <= curr.r) {
        merges[++idx] = curr.l;
        merges[++idx] = curr.r;
    }
    merges[++idx] = 1e9 + 10;
 
    int alive = 0;
    for (int i = 1; i <= n; i++) {
        int l = 0, r = idx;
        while (l < r) {
            int mid = (l + r) / 2;
            if (merges[mid] >= i) r = mid;
            else l = mid + 1;
        }
 
        if (merges[l] > i && merges[l - 1] < i  &&  l%2) {
			alive++;
        }
    }
 
    cout << alive << endl;
 
    return 0;
}

官方解

#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;

const int N = 1000010;
int a[N];
int n;

int backmax[N];


int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) cin >> a[i];
	memset(backmax, 0x3f, sizeof backmax);

	int ans = 0;
	//逆序求 n到当前i的 爪子能覆盖到的最大位置(也是最小坐标值)
	for (int i = n; i >= 1; i--) {
		backmax[i] = backmax[i + 1];
		if (i < backmax[i]) ans++;
		backmax[i] = min(backmax[i], i - a[i]);
		//爪子太长 负数了 haha
		backmax[i] = max(0, backmax[i]);
	}

	cout << ans << endl;

	return 0;
}

posted on   itdef  阅读(1)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
历史上的今天:
2021-02-18 剑指 Offer 39. 数组中出现次数超过一半的数字
2021-02-18 剑指 Offer 38. 字符串的排列 dfs
2021-02-18 剑指 Offer 37. 序列化二叉树 && 297. 二叉树的序列化与反序列化 bfs
2015-02-18 春节记录

导航

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示