题目
题目描述
某校大门外长度为 l 的马路上有一排树,每两棵相邻的树之间的间隔都是 1 米。我们可以把马路看成一个数轴,马路的一端在数轴 0 的位置,另一端在 l 的位置;数轴上的每个整数点,即 0,1,2,…,l,都种有一棵树。
由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。
输入格式
第一行有两个整数,分别表示马路的长度 l 和区域的数目 m。
接下来 m 行,每行两个整数 u,v表示一个区域的起始点和终止点的坐标。
输出格式
输出一行一个整数,表示将这些树都移走后,马路上剩余的树木数量
输入样例
500 3 150 300 100 200 470 471
输出样例
298
题解
1. N≤10000, M≤10000
这个显然很简单,暴力算法就可以过,复杂度
那这里也不多说,给个代码:
2. N≤100000, M≤100000
现在,过不了了,这时候,我们可以运用到差分算法。
我们知道有个东西叫前缀和,公式是这样的:
那我们把前缀和反过来,我们去维护 数组,最后,再去用前缀和恢复数组。
而当我们要挖掉一段树的时候,我们的差只在第一个地方和最后一个地方发生改变,所以,要想改变一部分,只需要的算法,所以整体是的复杂度。
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main () {
freopen ("tree.in", "r", stdin);
freopen ("tree.out", "w", stdout);
int n, m;
cin >> n >> m;
int a [1000010];
memset (a, 0, sizeof (a));
while (m --) {
int i, j;
cin >> i >> j;
if (i > j) swap (i, j);
a [i + 1] ++;
a [j + 2] --;
}
int k [1000010];
memset (k, 0, sizeof (k));
int cnt = 0;
for (int i = 1; i <= n + 1; i ++) {
k [i] = k [i - 1] + a [i];
if (k [i] <= 0) cnt ++;
}
cout << cnt;
return 0;
}
3. N≤1000000000, M≤100000
此时,上面的方法好似行不通,毕竟空间复杂度太大,不过还是可以拿个40几分的。
这时候,我们可以先以左端点进行排序,并从左向右计算每个端点,若有一个端点的左端点大于这个端点的右端点,则把那个左端点改为这个右端点+1,这样就可以在不开数组的情况下完成了。最后,再加在一起算个总和。
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct tr {
int x, y;
};
bool cmp (tr a, tr b) {
if (a.x <= b.x) return true;
else return false;
}
int main () {
freopen ("tree2.in", "r", stdin);
freopen ("tree2.out", "w", stdout);
int n, m;
cin >> n >> m;
tr a [100010];
memset (a, 0, sizeof (a));
for (int i = 0; i < m; i ++) {
cin >> a [i].x >> a [i].y;
if (a [i].x > a [i].y) swap (a [i].x, a [i].y);
}
sort (a, a + m, cmp);
for (int i = 0; i < m - 1; i ++) {
if (a [i].y < a [i].x) continue;
int k = 1;
for (int j = i + 1; j < m; j ++) {
if (a [i].y > a [j].x)
a [j].x = a [i].y + 1;
}
}
int cnt = n + 1;
for (int i = 0; i < m; i ++) {
if (a [i].y - a [i].x >= 0)
cnt -= a [i].y - a [i].x + 1;
}
cout << cnt;
return 0;
}