Codeforces Round #535 (Div. 3) E2. Array and Segments (Hard version)

本文解析了 Codeforces 平台上的 E2 题目,介绍了一种利用线段树优化时间复杂度的算法策略,通过选择特定区间使数组中最大值与最小值之差最大化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 题目链接:http://codeforces.com/contest/1108/problem/E2-
  • 题意:给你一个大小为n(1<=n<=1e5) 的数组a,给你m(m<=300)个区间 [l, r],你可以选择m个区间中的任意个区间,每个区间会使a中对应位置的数减一。问如何选择区间让数组a中的最大值和最小值的差值最大。
  • 思路:在选择完区间后,最终结果中的a中显然必然存在最小值和最大值,而对于最小值,必然有:所有包含最小值的区间都被选择了。可以这样证明:
    • 如果某个区间不包含最终最大值(最终结果中的最大值),而包含最终最小值(最终结果中的最小值),那么显然选择这个区间可以让差值趋向最大。
    • 如果某个区间不包含最终最大值,也不包含最终最小值,那么选不选择这个区间都没有影响。
    • 如果某个区间既包含最终最大值,也包含最终最小值,那么选不选择这个区间都没影响。
    • 那么只要选择包含最终最小值的所有区间,就能保证差值最大。通过遍历a中的每个元素,对于每个元素,假设其为最终最小值,选择包含其的所有区间并执行减一操作,然后找出整个a中的最大值和最小值的差值。这样就能找出最大差值。
  • 注意:遍历a时,到达某区间左端点时,将该区间的对应a[i]加一,离开某区间右端点
    后将该区间对应a[i]减一。否则会TLE。另外求最大最小值可以用线段数降低时间复杂度。
#include <bits/stdc++.h>
#define pi acos(-1)
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int INF = 0x3f3f3f3f;
const LL ll_INF = 0x3f3f3f3f3f3f3f3f;
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
//#define maxn 100007  //元素总个数
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
LL maxx[maxn<<2], add[maxn<<2];
LL minn[maxn<<2];
LL a[maxn]; //int n, m;
vector<int> in[maxn], out[maxn], usd;
PII p[310];


void PushUp(int rt)
{
    maxx[rt] = max(maxx[rt<<1], maxx[rt<<1|1]);
    minn[rt] = min(minn[rt<<1], minn[rt<<1|1]);
}

void PushDown(int rt, int ln, int rn) {
    if(add[rt]){
        add[rt<<1] += add[rt];
        add[rt<<1|1] += add[rt];
        maxx[rt<<1] += add[rt];
        maxx[rt<<1|1] += add[rt];
        minn[rt<<1] += add[rt];
        minn[rt<<1|1] += add[rt];
        add[rt]=0;
    }
}

void Build(int l, int r, int rt) {
    if(l==r){
        maxx[rt]=a[l];
        minn[rt]=a[l];
        return;
    }
    int mid = (l+r)>>1;
    Build(l, mid, rt<<1);
    Build(mid+1, r, rt<<1|1);
    PushUp(rt);
}

void update(int L, int R, LL C, int l, int r, int rt) {
    if(L<=l && r<=R){
        maxx[rt]+=C;
        minn[rt]+=C;
        add[rt]+=C;
        return ;
    }
    int mid = (l+r)>>1;
    PushDown(rt, mid-l+1, r-mid);
    if(L <= mid) update(L, R, C, l, mid, rt<<1);
    if(R >  mid) update(L, R, C, mid+1, r, rt<<1|1);
    PushUp(rt);
} //区间查询
PLL Query(int L, int R, int l, int r, int rt) {
    if(L<=l && r<=R){
        return PLL(maxx[rt], minn[rt]);
    }
    int mid = (l+r)>>1;
    PushDown(rt, mid-l+1, r-mid);
    LL big = 0, small = 0;
    PLL lpart = Query(L, R, l, mid, rt<<1);
    PLL rpart = Query(L, R, l, mid+1, rt<<1|1);
    big = max(lpart.first, rpart.first);
    small = min(lpart.second, rpart.second);
    return PLL(big, small);
    /*
    LL ans=0;
    if(L<=mid) ans += Query(L, R, l, mid, rt<<1);
    if(R> mid) ans += Query(L, R, mid+1, r, rt<<1|1);
    return ans;
    */
}

void change(int l, int r, int num)
{
    for(int i=l; i<=r; i++) a[i]+=num;
}

int main() {
    fastio;
    int n, m;
    cin >> n >> m;
    for(int i=1; i<=n; i++){
        cin >> a[i];
    }
    Build(1, n, 1);
    for(int i=1; i<=m; i++){
        int l,r;
        cin >> l >> r;
        p[i].first = l;
        p[i].second = r;
        in[l].push_back(i);
        out[r+1].push_back(i);
    }
    LL ansmax = a[1], ansmin = a[1], ansd=0, ansp=0;
    for(int i=2; i<=n; i++){
        ansmax = max(ansmax, a[i]);
        ansmin = min(ansmin, a[i]);
    }
    ansd = ansmax - ansmin;


    for(int i=1; i<=n; i++){
        for(int j=0; j<(int)in[i].size(); j++){
            int idx = in[i][j];
            update(p[idx].first, p[idx].second, -1, 1, n, 1);
        }
        for(int j=0; j<(int)out[i].size(); j++){
            int idx = out[i][j];
            update(p[idx].first, p[idx].second, 1, 1, n, 1);
        }
        if(!in[i].empty() || !out[i].empty()){
            ansmax = a[1];
            ansmin = a[1];
            /*for(int j=2; j<=n; j++){
                ansmax = max(ansmax, a[j]);
                ansmin = min(ansmin, a[j]);
            }*/
            PLL ansm = Query(1, n, 1, n, 1);
            ansmax = ansm.first;
            ansmin = ansm.second;
            if(ansmax - ansmin >= ansd){
                ansd = ansmax - ansmin;
                ansp = i;
            }
        }
    }

    cout << ansd << endl;
    for(int i=1; i<=m; i++){
        if(p[i].first <= ansp && ansp <= p[i].second) usd.push_back(i);
    }
    cout << (int)usd.size() << endl;
    for(int i=0; i<(int)usd.size(); i++){
        cout << usd[i];
        if(i == (int)usd.size() - 1) cout <<'\n';
        else cout << ' ';
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值