2022河南萌新联赛第(一)场 :河南工业大学 H(二分st表,离线线段树)

2022河南萌新联赛第(一)场 :河南工业大学 H(二分st表,离线线段树)

题意

同学们在实验室玩游戏,每个人有一个兴奋值 \(a_i\) ,但是这时候教练走过来了。教练有多次询问,每次会询问在一个区间的最大兴奋值。每个询问会影响在当前询问区间 [l,r][l,r][l,r] 内的人的兴奋值,只影响这次询问,每个人的兴奋值会因为这次询问变成 \(min(a_i,i−l+1)\)

思路一

考虑每个点的贡献情况,只有当 \(a_i < i - l+1\)\(a_i\) 才有贡献,答案的值域就被限制在 \([1,r - l+1]\) 内。

注意到 答案的上界具有单调性 。我们考虑当确定一个最大值 \(max\) 时,\(max\) 总可以表示为 \(i-l+1\) 的形式,以 \(i\) 为分界,\([l,i]\) 的区间不可能有更优值(上界的单调性),对 \([i+1,r]\) ,如果区间最大值比 \(max\) 大,那么选取更大肯定更优。

因此st表二分答案即可。


这个思路感觉没见过,这里虽然 \(a_i\) 不具有单调性,但是 \(i-l+1\) 有单调性并且其还是答案上界,每一个可能解都可以映射到一个 \(i-l+1\) 上面。进一步的发现可以通过 \(i\) 分割区间,得到很好的二分性质。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 2e5 ,mod=1e9 + 7;

void solve()
{    
    int n,m; cin >> n >> m;
    vector<int> a(n + 1);
    rep(i,1,n) cin >> a[i];
    vector<vector<int>> f(n + 1,vector<int>(21));
    for(int i = 1;i <= n;i += 1) f[i][0] = a[i];
    for(int j = 1;j <= 20;j += 1) {
        for(int i = 1;i + (1 << j) - 1 <= n;i += 1) {
            f[i][j] = max(f[i][j - 1], f[i + (1 << j - 1)][j - 1]);
        }
    }
    auto getMax = [&](int l,int r) {
        if(l > r) return -1ll * linf;
        int k = __lg(r - l + 1);
        return max(f[l][k], f[r - (1 << k) + 1][k]);
    };
    
    auto query = [&](int ql,int qr) {
        // [l,r]  i - l + 1 [i,r]
        int l = ql - 1,r = qr + 1;
        auto chk = [&](int pos) {
            return pos - ql + 1 <= getMax(pos,qr);
        };
        while(l + 1 < r) {
            int mid = l + r >> 1;
            if(chk(mid)) l = mid;
            else r = mid;
        }
        return l - ql + 1;
    };
    while(m -- ) {
        int l,r; cin >> l >> r;
        cout << query(l,r) << ' ';
    }
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    //int T;cin>>T;
    //while(T--)
        solve();

    return 0;
}

思路二

考虑每一个点的贡献,可以发现当 \(l \ge i+1-a_i\) 时贡献来自下标 \(i\) 。并不会因为 \(l\) 的增大变回 \(a_i\) 。因此我们也可以考虑按 \(l\) 从小到大依次处理询问。

考虑如何维护每个点的贡献。当达到边界条件 \(l = i+1-a_i\) 时应该切换贡献。对等式右边,我们可以开桶把值为 \(i+1-a_i\) 的下标放一起。之后处理询问时枚举所有 \(l\) ,当达到对应边界条件,更新这些点。


这个思路可以实现的关键还是在于不等式的单调性和询问区间 \(l\) 和变化条件有关。当枚举 \(i+1-a_i\) 时就相当于对询问区间的枚举。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<set>
#include<queue>
#include<map>
#include<stack>
#include<string>
#include<random>
#include<iomanip>
#define yes puts("yes");
#define inf 0x3f3f3f3f
#define ll long long
#define linf 0x3f3f3f3f3f3f3f3f
#define ull unsigned long long
#define endl '\n'
#define int long long
#define rep(i,a,n) for(int i = a;i <= n;i++)
#define per(i,n,a) for(int i = n;i >= a;i--)
using namespace std;
mt19937 mrand(random_device{}());
int rnd(int x) { return mrand() % x;}
typedef pair<int,int> PII;
const int MAXN =10 + 1e5 ,mod=1e9 + 7;
int n,q;
int a[MAXN];
struct SegTree {
    #define TRMAXN 100010
    #define lc (p << 1) 
    #define rc (p << 1 | 1)
    struct SNode {
        int l,r;
        // TODO: 定义需要维护的数据
        int mxv,mxl;
        // TODO: 设置tag
        int tagV,tagL;
        SNode() {
            l = r = 0;
            // TODO: 数据构造
            mxv = mxl = -inf;
            tagV = tagL = -inf;
        }
        void init(int x) {
            l = r = x;
            // TODO: 单点初始化数据
            mxv = a[x];
            mxl = -inf;
            tagV = tagL = -inf;
        }
        SNode operator+ (const SNode& rv) const {
            SNode t; 
            // [l,r]
            t.l = l, t.r = rv.r;
            // TODO: 左右儿子合并操作+
            t.mxv = max(mxv, rv.mxv);
            t.mxl = max(mxl, rv.mxl);
            return t;
        }
    }tr[TRMAXN << 2];
    void pushup(int p) {
        tr[p] = tr[lc] + tr[rc];
    }
    void upd(int p,int val) {
        // 数据更新
        tr[p].mxv = -inf;
        tr[p].mxl = val;
        // 标签修
    }
    void pushdown(int p) { // 标签下放
        
    }
    // 下面的大多数时候不用改
    void build(int l,int r,int p = 1) {
        if(l == r) return tr[p].init(l);
        int mid = l + r >> 1;
        build(l,mid,lc);
        build(mid + 1,r,rc);
        pushup(p);
    }
    void modify(int l,int r,int val,int p = 1) {
        int L = tr[p].l, R = tr[p].r;
        if(l <= L && R <= r) {
            upd(p,val);
            return;
        }
        pushdown(p);
        int mid = L + R >> 1;
        if(l <= mid) modify(l,r,val,lc);
        if(r > mid) modify(l,r,val,rc);
        pushup(p);
    }
    void modify(int tar,int val) {return modify(tar,tar,val);}

    SNode query(int l, int r,int p = 1) {
        int L = tr[p].l, R = tr[p].r;
        if(l <= L && R <= r) return tr[p];
        pushdown(p);
        int mid = L + R >> 1;
        if(l <= mid && r > mid) return query(l,r,lc) + query(l,r,rc);
        if(l <= mid) return query(l,r,lc);
        return query(l,r,rc);
    }
    SNode query(int tar) {return query(tar,tar);}
}tr;
void solve()
{    
    cin >> n >> q;
    for(int i = 1;i <= n;i += 1) cin >> a[i];
    vector<vector<int>> b(n + 1);
    for(int i = 1;i <= n;i += 1) b[max(0ll, i + 1 - a[i])].push_back(i);
    tr.build(1,n);
    vector<vector<PII>> qs(n + 1);
    for(int i = 1;i <= q;i += 1) {
        int l,r; cin >> l >> r;
        qs[l].push_back({r,i});
    }
    vector<int> ans(q + 1);
    for(int i = 0;i <= n;i += 1) {
        for(auto pos : b[i]) tr.modify(pos,pos);
        for(auto [j,id] : qs[i]) {
            auto res = tr.query(i,j);
            ans[id] = max(res.mxl - i + 1, res.mxv);
        }
    }
    for(int i = 1;i <= q;i += 1) cout << ans[i] << " ";
}
signed main()
{
    ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);

    //int T;cin>>T;
    //while(T--)
        solve();

    return 0;
}
posted @ 2022-07-13 12:00  Mxrurush  阅读(21)  评论(0编辑  收藏  举报