题解:P10837 『FLA - I』云音泛

你说得对,但是就是喜欢用离散化。

题目可以简化为 \(n\) 条长度一定线段覆盖,问只被一条线段覆盖的点的个数。

由于只能移动一条线段,所以我们可以计算出每一条线段移动最多能够增加的贡献,然后取最大的。

首先对于一条线段,由于其可以任意移动,那么最有效的方式自然是让它覆盖一个未被覆盖的区间,这样移动后的答案是最大的。

考虑移动第 \(i\) 条线段对答案产生的影响。首先这条线段原来覆盖的区域覆盖数要减一。记区间内移动前被两条线段覆盖的点数为 \(cnt2_i\),移动前被一条线段覆盖的点数为 \(cnt1_i\),则答案减少 \(cnt1_i\),增加 \(cnt2_i\)

同时还有移动后,这条线段覆盖了一个原本未被覆盖的区间,所以答案还要加上线段长度 \(m\)

总结一下,答案变为

\[ans - cnt1_i + cnt2_i + m \]

取最大值即可。

计算某个点上有多少条线段覆盖,可以用树状数组区间修改,单点查询。当然线段树也可以。但是谁会拒绝一个又好写又常数小的可爱的树状数组呢?

但是我们发现这样做理论上树状数组应该开到 \(t_i + m\),这样是开不下的。

于是我们使用传统艺能,离散化。

但是离散化不能只记录线段的两个端点,例如下面这个例子:两条线段分别覆盖区间 \([2,5]\)\([7,10]\)

离散化后我们记录的值是:\(1,1,1,1\)

我们将 \([5,7]\) 也认为是有线段覆盖的了。

因此我们需要一个标记,作为左右边界的标记。一个简单的办法就是将 \(l - 1\)\(r + 1\) 也放入离散化中。这样离散化后,上例的答案就变为:\(0,1,1,0,0,1,1,0\),将两个不连续的区间分开来了。

具体实现见代码:

#include<bits/stdc++.h>
#define MAXN 400010
using namespace std;
typedef long long ll;
ll n, m, tot;
ll lt[MAXN], rt[MAXN];
ll sum[MAXN << 2], t[MAXN << 2], cnt1[MAXN << 2], cnt2[MAXN << 2], res[MAXN << 2];
ll low_bit(ll x){ return x & (-x); }
void add(ll i, ll k){
    while(i <= tot){
        sum[i] += k;
        i += low_bit(i);
    }
}
ll query(ll i){
    ll res = 0;
    while(i > 0){
        res += sum[i];
        i -= low_bit(i);
    }
    return res;
}
void update(ll l, ll r, ll x){
    add(l, x);
    add(r + 1, -x);
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i = 1; i <= n; i++) scanf("%lld",&lt[i]);
    for(ll i = 1; i <= n; i++) rt[i] = lt[i] + m - 1;
    for(ll i = 1; i <= n; i++){
        t[++tot] = lt[i] - 1;
        t[++tot] = lt[i];
        t[++tot] = rt[i];
        t[++tot] = rt[i] + 1;
    }
    sort(t + 1, t + tot + 1);
    tot = unique(t + 1, t + tot + 1) - t - 1;
    for(ll i = 1; i <= n; i++) lt[i] = lower_bound(t + 1, t + tot + 1, lt[i]) - t;
    for(ll i = 1; i <= n; i++) rt[i] = lower_bound(t + 1, t + tot + 1, rt[i]) - t;
    for(ll i = 1; i <= n; i++){
        update(lt[i], rt[i], 1);
    }
    for(ll i = 1; i <= tot; i++) res[i] = query(i);
    for(ll i = 2; i <= tot; i++){
        if(res[i] == 1 && res[i - 1] >= 1) cnt1[i] = t[i] - t[i - 1];
        else if(res[i] == 1 && res[i - 1] < 1) cnt1[i] = 1;
        else if(res[i - 1] == 1 && res[i] >= 1) cnt1[i] = t[i] - t[i - 1] - 1;
    }

    for(ll i = 2; i <= tot; i++){
        if(res[i] == 2 && res[i - 1] >= 2) cnt2[i] = t[i] - t[i - 1];
        else if(res[i] == 2 && res[i - 1] < 2) cnt2[i] = 1;
        else if(res[i - 1] == 2 && res[i] >= 2) cnt2[i] = t[i] - t[i - 1] - 1;
    }
    ll ans = 0;
    for(ll i = 1; i <= tot; i++) ans += cnt1[i];
    for(ll i = 1; i <= tot; i++) cnt1[i] += cnt1[i - 1];
    for(ll i = 1; i <= tot; i++) cnt2[i] += cnt2[i - 1];
    ll maxx = 0;
    for(ll i = 1; i <= n; i++){
        ll c1 = cnt1[rt[i]] - cnt1[lt[i] - 1], c2 = cnt2[rt[i]] - cnt2[lt[i] - 1];
        ll tmp = c2 + m - c1;
        maxx = max(maxx, tmp);
    }
    printf("%lld\n",ans + maxx);
    return 0;
}

记得开 long long

posted @ 2024-09-26 18:05  Night_Tide  阅读(9)  评论(0编辑  收藏  举报