首页 > 其他分享 >CF765F,CF1793F,JSOI2009:区间最接近的两数

CF765F,CF1793F,JSOI2009:区间最接近的两数

时间:2024-03-20 19:55:05浏览次数:27  
标签:node default val CF1793F JSOI2009 tr mid int 两数

link:https://codeforces.com/contest/765/problem/F
据说是典中典问题(出现三次了)
题意:给一个序列 \(a_1,\dots,a_n\),有 \(m\) 次询问,每次询问给 \(l,r(1\leq l<r\leq n)\)问 \(\min_{l\leq s<t\leq r}|a_s-a_t|\)
\(1\leq n,m\leq 10^5,a_i\leq 10^9\).


思路

这个做法还是很妙,想题还是先想暴力,最暴力当然是每次询问 \(O(n^2)\) 回答,太暴力了没什么用。
题目不强制在线(也没法强制),给询问离线是常见操作,离线下来随便按照右端点 \(r\) 排序,每次考虑加入一个数 \(a_i\) 产生的影响,以及如何回答所有以 \(i\) 为右端点的询问。至此应当是非常经典的套路。

这种序对的问题,可以只考虑一侧的关系(另一侧是对称的),设 \(f_i^{(r)}=\min_{i<j\leq r} |a_j-a_i|\),扫描到 \(r\) 时查询区间最小值。那么加入一个新的元素 \(a_i\) 会如何对之前的 \(f_i\) 产生影响呢?

  • \(j<i\) 且 \(a_j>a_i\),找到满足这一条件的最大的 \(j\)(通过一棵值域线段树,\(tr[a_i]=i\),查询区间 \([a_i+1,+\infty)\) 内的最大值),然后做单点修改。而这题的关键就在于这样的 \(j\) 并不多:
    • 设\(k<j<k\),若 \(a_k>a_j>a_i\),则 \(f_k\) 在插入 \(a_j\) 的时候已经被更新过了,此时不会更优;
    • 只考虑 \(a_j>a_k>a_i\),如果要 \(a_i\) 能够影响到 \(k\),则需要 \(a_j-a_k>a_k-a_i\),即 \(a_k<\frac{a_i+a_j}{2}\),带上之前的条件,就是 \(a_k\in [a_i,\lfloor \frac{a_i+a_j}{2}\rfloor]\) ,以此类推。
    • 区间长度的上界如何变化:$$\frac{a_j-a_i}{2}\to \frac{a_k-a_i}{2}<\frac{a_j-a_i}{4}\to \dots$$
    • \(O(\log V)\) 次就能跳完
  • 对于\(j<i,a_j<a_i\) 的情况类似处理,同样是找最大值(因为 \(j<i\) 的限制)
    所以只需要两棵线段树:
  • 值域线段树,维护为某值下标,单点赋值,区间取max
  • 维护 \(f\) 的线段树,单点修改,区间取min
    如果有两个数相同怎么办?维护每个数\(a_i\) 上一次相同值出现的位置\(g_i\),如果区间最小值 \(\geq l\) 则存在一对相同的数,答案为0
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define endl '\n'
#define fastio ios_base::sync_with_stdio(false);cin.tie(0);cout.tie(0)
using namespace std;
const int N=1e5+5;
const int MAX_A=1e9+5;
const int MAX_Q=1e5+5;
const int INF=0x3f3f3f3f;
struct Query{int l,r,idx,ans;}q[MAX_Q];
template<class T>
class Node{
public:
    int lson,rson;
    T v;
};
template<class T,typename Fun,int LOG=35>
class SegT{
    Fun f;
    Node<T> tr[N*LOG];
    T default_val;
    int cnt_node,rt,n;
    #define ls (tr[node].lson)
    #define rs (tr[node].rson)
public:
    SegT(Fun g,T v):f(g),default_val(v),rt(0){tr[0].v=default_val;}
    void init(int MX){n=MX;}
    void push_up(int node){tr[node].v=f(tr[ls].v,tr[rs].v);}
    void modify(int &node,int l,int r,int x,int v){
        if(!node){
            node=++cnt_node;
            tr[node].v=default_val;
        }
        if(l==r){
            tr[node].v=f(tr[node].v,v);
            return;
        }
        int mid=(l+r)>>1;
        if(mid>=x)modify(ls,l,mid,x,v);
        else modify(rs,mid+1,r,x,v);
        push_up(node);
    }
    T query(int node,int l,int r,int ql,int qr){
        if(!node)return default_val;
        if(ql<=l&&r<=qr)return tr[node].v;
        int mid=(l+r)>>1;
        T ret=default_val;
        if(mid>=ql)ret=f(ret,query(ls,l,mid,ql,qr));
        if(mid+1<=qr)ret=f(ret,query(rs,mid+1,r,ql,qr));
        return ret;
    }
    void modify(int x,int v){modify(rt,1,n,x,v);}
    T query(int L,int R){return query(rt,1,n,L,R);}
};
auto mx=[](int x,int y){return x>y?x:y;};
SegT ind(mx,0),g(mx,0);
SegT f([](int x,int y){return x<y?x:y;},INF);
int main(){
    fastio;
    int n,m;
    cin>>n>>m;
    auto a=vector<int>(n+1),lst=vector<int>(n+1);
    ind.init(MAX_A);f.init(n);g.init(n);
    map<int,int> occ;
    rep(i,1,n){
        cin>>a[i];
        if(occ.count(a[i]))lst[i]=occ[a[i]];
        else lst[i]=0;
        occ[a[i]]=i;
        g.modify(i,lst[i]);
    }
    rep(i,1,m){
        cin>>q[i].l>>q[i].r;
        q[i].idx=i;
    }
    sort(q+1,q+m+1,[](Query q1,Query q2){return q1.r<q2.r;});
    int cur=0;
    rep(i,1,n){
        ind.modify(a[i],i);
        int L=1,R=MAX_A,j;
        while(a[i]<R){
            j=ind.query(a[i]+1,R);
            if(!j)break;
            f.modify(j,a[j]-a[i]);
            R=(a[i]+a[j])/2;
        }
        while(L<a[i]){
            j=ind.query(L,a[i]-1);
            if(!j)break;
            f.modify(j,a[i]-a[j]);
            L=(a[i]+a[j]+1)/2;
        }
        while(cur+1<=m&&q[cur+1].r==i){
            cur++;
            q[cur].ans=f.query(q[cur].l,q[cur].r);
            // if(g.query(q[cur].l,q[cur].r)>=q[cur].l)q[cur].ans=0;
        }
    }
    sort(q+1,q+m+1,[](Query q1,Query q2){return q1.idx<q2.idx;});
    rep(i,1,m)cout<<q[i].ans<<endl;
    return 0;
}

标签:node,default,val,CF1793F,JSOI2009,tr,mid,int,两数
From: https://www.cnblogs.com/yoshinow2001/p/18085949

相关文章

  • 1. 两数之和
    1.两数之和力扣题目链接(opensnewwindow)给定一个整数数组nums 和一个目标值target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。示例:给定nums=[2,7,11,15],......
  • Leecode 求两数之和
    Day1刷题我的解题思路是按照第一个元素往后排,不重复的找相加的组合,判断是否为target值,时间复杂度较高,为\(\mathcal{O}(n^2)\)。classSolution{publicint[]twoSum(int[]nums,inttarget){intflag=1;while(flag==1){for(in......
  • LeetCode01.两数之和
    ques:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。示例1:输入:nums=......
  • leetcode 2.两数相加 ,链表
    2.两数相加给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字0之外,这两个数都不会以0 开头。 示例1:输入:l1=[2,4......
  • abc331E 两数组元素间带限制的最大和
    题面:给定大小为n的数组A,大小为m的数组B,那么共有n*m个元素和。现给出L对禁用下标(a,b),找一对不在L中的下标(i,j),使用A[i]+B[j]最大,求该最大值。范围:n,m<=1e5;1<=L<=min(1e5,nm-1)思路:先对A和B按从大到小排序,然后让i指向A起始位置,j指向B起始位置,将对应的四元组(sum,i,j,flag)加入......
  • abc238D 两数之和跟按位与
    给定非负整数a和s,问是否存在一组非负整数(x,y),满足x&y=a,并且x+y=s?数据范围:0<=a,s<2^60思路:异或是不进位加法,如果考虑进位,加上按位与的结果左移1位即可,也就是:x+y=(x^y)+((x&y)<<1),代入得x^y=s-2a,并且x&y=a,逐位分析可知,按位与的结果为1时,异或结果必为0。#include<bits/stdc++.h>......
  • LeetCodeHot100 1.两数之和 46.字母异位词分组 128.最长连续序列
    1.两数之和https://leetcode.cn/problems/two-sum/description/?envType=study-plan-v2&envId=top-100-likedpublicint[]twoSum(int[]nums,inttarget){HashMap<Integer,Integer>map=newHashMap<>();for(inti=0;i<nums.l......
  • 代码随想录 第六天 哈希表理论基础 ● 242.有效的字母异位词 ● 349. 两个数组的交
    LeetCode:242.有效的字母异位词-力扣(LeetCode)思路:既然只判断两个字符串的字母,就一个++,一个--,最后如果二十六个字母都是零,说明两个字符串相等。反思: //charat(i)是返回字符串索引,所以s.charAt(i)-'a'实际上是获取字符串s中第i个字符相对于字母'a'的偏移量。......
  • 两数之和-输出有序数组
    167TwoSumII-Inputarrayissorted问题描述:给定一个已按照升序排列的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值index1和index2,其中index1必须小于index2。说明:返回的下标值(index1和index2)不是从零开始的。你可以假设每个输入......
  • 代码随想录算法训练营第六天 |242. 有效的字母异位词 349. 两个数组的交集 202. 快乐
    1.两数之和 已解答简单 相关标签相关企业 提示 给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同......