P10837 『FLA - I』云音泛
题目描述
在梦中,秋种下了 n n n 朵凋零玫瑰。他记得,第 i i i 朵玫瑰是在时刻 t i t_i ti 种植的。
凋零玫瑰在被种下的那个时刻就立即开放,但每一株玫瑰只会开放 m m m 个时刻(在时刻 T T T 种植的玫瑰会且仅会在从时刻 T T T 到时刻 T + m − 1 T+m-1 T+m−1 的 m m m 个时刻开放),在 m m m 个时刻后便化作再也无法挽留的灰尘,飘散在凛冽的寒风中。
他问你,假如他可以改变不超过一朵玫瑰的种植时间(选定一个 t i t_i ti 并将其修改为任意正整数),那么最多有多少个时刻有且仅有一株凋零玫瑰开放?
输入格式
第一行输入两个正整数 n , m n,m n,m。
第二行输入 n n n 个正整数,第 i i i 个正整数为 t i t_i ti。
输出格式
输出一行一个正整数表示答案。
样例 #1
样例输入 #1
5 4
11 9 1 3 12
样例输出 #1
14
样例 #2
样例输入 #2
13 7
6 42 58 41 20 60 2 61 45 28 45 28 12
样例输出 #2
38
提示
「样例解释 #1」
如图,使用金色标记有且仅有一株凋零玫瑰开放的时刻,使用黑色和红色标记每朵凋零玫瑰开放的时刻。
将使用红色标记的玫瑰的种植时刻改为 17 17 17(将 t 1 t_1 t1 的值修改为 17 17 17,如下图)后有 14 14 14 个时刻有且仅有一株凋零玫瑰开放。可以证明不存在能够使有且仅有一株凋零玫瑰开放的时刻数量大于 14 14 14 的修改方案。
「数据范围」
测试点编号 | n ≤ n \leq n≤ | m ≤ m \leq m≤ | t i ≤ t_i \leq ti≤ |
---|---|---|---|
1 ∼ 6 1 \sim 6 1∼6 | 5000 5000 5000 | 5000 5000 5000 | 5000 5000 5000 |
7 ∼ 12 7 \sim 12 7∼12 | 2 × 1 0 5 2 \times 10^5 2×105 | 2 × 1 0 5 2 \times 10^5 2×105 | 2 × 1 0 5 2 \times 10^5 2×105 |
13 ∼ 14 13 \sim 14 13∼14 | 2 × 1 0 5 2 \times 10^5 2×105 | 1 1 1 | 1 0 9 10^9 109 |
15 ∼ 20 15 \sim 20 15∼20 | 2 × 1 0 5 2 \times10^5 2×105 | 1 0 9 10^9 109 | 1 0 9 10^9 109 |
对于所有测试数据, 1 ≤ n ≤ 2 × 1 0 5 1 \leq n \leq 2 \times 10^5 1≤n≤2×105, 1 ≤ m , t i ≤ 1 0 9 1 \leq m,t_i \leq 10^9 1≤m,ti≤109。
思路:
原本准备离散化差分来解决的,但满不了分。
直接贪心即可:
首先,代码对玫瑰的种植时间进行排序。
然后,代码遍历排序后的玫瑰种植时间,计算相邻玫瑰种植时间之间的间隔,并累加这些间隔中有且仅有一株凋零玫瑰开放的时刻数量。
接着,代码计算修改每个玫瑰种植时间可以获得的增益,并记录最大的增益。
最后,代码输出初始时刻数量加上最大的增益,即为最终答案。
具体来说,代码中的变量 ans 表示初始时刻数量,即在不修改任何玫瑰种植时间的情况下,有且仅有一株凋零玫瑰开放的时刻数量。变量 l 和 r 分别表示当前玫瑰种植时间对应的开放时间段的左右边界。变量 add 表示最大的增益,即通过修改玫瑰的种植时间可以获得的最大增益。
代码中的两层循环分别计算了初始时刻数量和最大的增益。第一层循环计算了初始时刻数量,第二层循环计算了最大的增益。最后,代码输出初始时刻数量加上最大的增益,即为最终答案。
代码:
#include <bits/stdc++.h>
#define endl '\n'
#define int long long
using namespace std;
//int a[200005][2];
//int ex[200005]={};
int t[200005];
signed main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int N,M;
// memset(ex,0,sizeof(ex)); // 初始化ex数组为0
unsigned long long ans = 0;
cin >> N >> M;
for (int i = 1; i <= N; i++) {
cin>>t[i];
// int x;
// cin >> x ;
// t[i+1]=x;
// m[x]++;
// m[x+M]--;
}
// int index=0;
// for(pair<int,int> e: m){ //进行离散化,将每段记录成数组,第一行为位置,第二行为长度
// a[index][1]=e.second;
// a[index++][0]=e.first;
// }
int add = -M;
sort(t+1,t+N+1); // 逆序排列玫瑰种植时间
t[0]= -0x3f3f3f3f3f3f3f3f; // 设置哨兵,避免数组越界
t[N+1]= 0x3f3f3f3f3f3f3f3f; // 设置哨兵,避免数组越界
for(int i=1;i<=N;i++){ // 遍历每个玫瑰的种植时间
long long l,r,a=0; // 定义变量l和r,分别表示当前玫瑰种植时间对应的开放时间段的左右边界,a表示当前玫瑰种植时间对应的增益
l=max(t[i-1]+M,t[i]); // 计算左边界
r=min(t[i]+M,t[i+1]); // 计算右边界
if(r>l) { // 如果左边界小于右边界
a-=(r-l); // 计算增益
ans+=(r-l); // 累加增益
}
if(i>=2 && i<=N-1){ // 如果当前玫瑰不是第一个也不是最后一个
if(t[i]<t[i-1]+M){ // 如果当前玫瑰与前一个玫瑰的间隔小于m
l=max(t[i-2]+M,t[i]); // 计算左边界
r=min(t[i-1]+M,t[i+1]); // 计算右边界
if(r>l) a+=(r-l); // 如果左边界小于右边界,计算增益
}
if(t[i]+M>t[i+1]){ // 如果当前玫瑰与后一个玫瑰的间隔小于m
r=min(t[i+2],t[i]+M); // 计算右边界
l=max(t[i+1],t[i-1]+M); // 计算左边界
if(r>l) a+=(r-l); // 如果左边界小于右边界,计算增益
}
}
else if(i==1){ // 如果当前玫瑰是第一个
if(t[i]+M>t[i+1]){ // 如果当前玫瑰与后一个玫瑰的间隔小于m
r=min(t[i+2],t[i]+M); // 计算右边界
l=max(t[i+1],t[i-1]+M); // 计算左边界
if(r>l) a+=(r-l); // 如果左边界小于右边界,计算增益
}
}
else if(i==N){ // 如果当前玫瑰是最后一个
if(t[i]<t[i-1]+M){ // 如果当前玫瑰与前一个玫瑰的间隔小于m
l=max(t[i-2]+M,t[i]); // 计算左边界
r=min(t[i-1]+M,t[i+1]); // 计算右边界
if(r>l) a+=(r-l); // 如果左边界小于右边界,计算增益
}
}
add=max(add,a); // 更新最大的增益
}
// // 计算初始时刻数量
// for(int i=1;i<index;i++){
// a[i][1]+=a[i-1][1];
// if(a[i-1][1] == 1 )
// ans += a[i][0]-a[i-1][0];
// }
// 输出最终答案
cout <<ans + M + add ;
return 0;
}