1. 程式人生 > 實用技巧 >5467. 找到最接近目標值的函式值

5467. 找到最接近目標值的函式值

題目大意

尋找陣列arr的區間l和r,使[l,r]區間內的數&後與target的差值的絕對值最小

思路

有一個很重要的推論就是&運算具有單調遞減性,也就是ask(a,r+1)<=ask(l,r)<=ask(l+1,r)。因此我們可以通過滑動陣列,當前區間結果>target時,r++;否則l++。
我們用線段樹維護區間&的值

class Solution {
public:
    static const int maxn=4*1e5+10;
    int a[maxn];
    int d[maxn];
    void build(int s,int t,int p)
    {
        //p代表線段樹的結點號,s和t描述原陣列的區間
        if(s==t)
        {
            d[p]=a[s];
            return;
        }
        int m=(s+t)>>1;
        build(s,m,2*p);
        build(m+1,t,2*p+1);
        d[p]=d[2*p]&d[2*p+1];
    }
    int ask(int l,int r,int s,int t,int p)
    {
        //l,r是查詢區間;s,t是當前線段樹的結點區間,p是線段樹當前訪問的結點
        if(l<=s&&r>=t)
            return d[p];
        int m=(s+t)>>1;
        if(r<=m)
            return ask(l,r,s,m,2*p);
        if(l>m)
            return ask(l,r,m+1,t,2*p+1);
        return ask(l,m,s,m,2*p)&ask(m+1,r,m+1,t,2*p+1);
    }
    int closestToTarget(vector<int>& arr, int target) {
        int n=arr.size();
        for(int i=0;i<n;i++)
            a[i]=arr[i];
        build(0,n-1,1);
        int l=0,r=0,ans=1e9+1e7;
        while(r<n)
        {
            int t=ask(l,r,0,n-1,1);
            if(t>target)
                r++;
            else
                l++,r=max(l,r);
            ans=min(ans,abs(t-target));
        }   
        return ans;
    }
};