• NKOJ P3545 接近 (单调队列/二分查找)


    时间限制 : 10000 MS   空间限制 : 165536 KB
    问题描述

    对于一个数字序列A,并且有若干询问。对于每个询问,要求求出一段在序列A中非空 的连续段使得这一段数字的总和的绝对值尽量接近P。

    输入格式

    第一行2个数N、T,表示序列的长度和询问的个数。
    接下来一行N个整数,表示A序列。 接下来T行,每行一个数P表示询问。

    输出格式

    共输出T行,每行对应一个询问的答案。
    输出3个数:第一个数为能够实现的最接近P 的数,后面两个数L、R表示A序列中的L到 R这一段数能实现这个答案。
    如果存在多解,输出L最小的解;
    如果还有多解,输出R最小的解。

    样例输入

    输入样例1
    5 1 
    -10 -5 0 5 10 
    3  

    样例输入2
    6 2 
    -2 5 3 0 -3 -4 

    6

    样例输入3
    7 2 
    -2 -1 -2 4 1 -3 7 

    6

    样例输出

    样例输出1
    5 2 2

    样例输出2
    1 1 6
    6 1 3
    样例输出3
    0 1 5 
    6 2 7

    提示

    【数据范围】
    30%的数据 1<=N<=1,000。
    60%的数据 1<=N<=10,000。
    100%的数据 1<=N<=100,000,A 序列中数字绝对值<=10,000,T<=100,询问的 数字<=10^9

     
     
    二分

    满足|sum[j]-sum[i-1]|>=p或者 |sum[j]-sum[i-1]|<=p
    我想起老板说过可以把绝对值打开
    打开后变一下形
    可以得到 sum[j]+p<=sum[i-1];
               或者 sum[j]-p<=sum[i-1];
               这时候我们就可以用重新对sum数组复制成struct sum2 数组 按照
               val值进行排序 当然复制的id 也要保留 也就是我们要 对复制的struct 结构体数组排序
               然后在对此数组的va值复制成sum3数组
               然后就是用j枚举N
               然后二分对sum[j]-p和sum[j]+p在sum3数组里面二分查找>=的最小值
               id记在sum2 结构体里面  且sum3 和sum2 下标相同
               ans=min{abs(abs(sum3[二分的下标]-sum[j])(-或者+)p)}
          
               {
                    r=j;
                    l=sum2[枚举的下标].id+1;// if(ans>abs(abs(sum3[下标]-sum[j])(+/-)p))

              }
              if(ans==abs(abs(sum3[下标]-sum[j])-/+p))
    {
    if(l==sum2[下标].id+1&&r>j)
    {
    l=sum2[下标].id+1;
    r=j;
    }
    if(l>sum2[下标].id+1)
    {
    l=sum2[下标].id+1;
    r=j;
    }
    }
    那么  cout<<abs(sum3[下标]-sum[j]);//符合条件的<<" "<<l<<" "<<r<<endl;
                                    
    //
    #include<bits/stdc++.h>
    using namespace std;
    #define maxnn 102000
    #define ll long long  
    ll sum[maxnn];
    ll sum3[maxnn];
    ll n,t;
    struct node
    {
        ll id,va;
        
    }sum1[maxnn];
    bool cmp(node a,node b)
    {
        if(a.va==b.va)
        return a.id<b.id;
        else
        return a.va<b.va;
        
    }
    int main()
    {
        cin>>n>>t;
        ll x;
        ll uu;
        ll p;
        ll ans=1000000000000;
        sum1[0].id=0;
        sum1[0].va=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&x);
            sum[i]=sum[i-1]+x;
            sum1[i].va=sum[i];
            sum1[i].id=i;
        }
        sort(sum1,sum1+1+n,cmp);
        for(int i=0;i<=n;i++)
        {
            sum3[i]=sum1[i].va;
        }
        for(int i=1;i<=t;i++)
        {
            ans=100000000000;
            ll l=10000000000,r=10000000000;
            cin>>p;
            for(int j=1;j<=n;j++)
            {
                int xx=lower_bound(sum3,sum3+1+n,sum[j]+p)-sum3;//j
                int xxx=lower_bound(sum3,sum3+1+n,sum[j]-p)-sum3;
                if(ans>abs(abs(sum3[xx]-sum[j])-p))
                {
                    uu=abs(sum3[xx]-sum[j]);
                    ans=abs(abs(sum3[xx]-sum[j])-p);
                    r=j;
                    l=sum1[xx].id+1;
                }
                if(ans==abs(abs(sum3[xx]-sum[j])-p))
                {
                    if(l==sum1[xx].id+1&&r>j)
                    {
                        l=sum1[xx].id+1;
                        r=j;
                    }
                    if(l>sum1[xx].id+1)
                    {
                        l=sum1[xx].id+1;
                        r=j;
                    }
                }
                
            if(ans>abs(abs(sum3[xxx]-sum[j])-p))
                {
                    uu=abs(sum3[xxx]-sum[j]);
                        ans=abs(abs(sum3[xxx]-sum[j])-p);
                        r=j;
                    l=sum1[xxx].id+1;
                }
                    if(ans==abs(abs(sum3[xxx]-sum[j])-p))
                {
                    if(l==sum1[xxx].id+1&&r>j)
                    {
                        l=sum1[xxx].id+1;
                        r=j;
                    }
                    if(l>sum1[xxx].id+1)
                    {
                        l=sum1[xxx].id+1;
                        r=j;
                    }
                }
                
            }
             cout<<uu<<" "<<l<<" "<<r<<endl;
             
        }
        
        
    }

     单调队列

    对每次询问都跑一遍。
    显然第一种思路是行不通的,预处理出所有区间前缀和之差就是 O(N2)
    。那么只有考虑第二种思路。鉴于数据范围,只能承受时间复杂度 O(NT)或者更快的算法。
    每次询问 O(N)
    ,那么容易想到单调队列。
    然而这样就有一个问题:前缀和数组并不满足单调性,这怎么办?
    回到问题本身。问题等价于下面的形式:求出一对(i,j)
    ,使得|sum[i]−sum[j]|尽量接近 P。注意到绝对值的形式,那么(i,j)可以是无序的。也就是
    说,i,j
    的大小关系在寻找答案没有影响,那么我们可以强行排序,就有单调性了。
    不妨将前缀和从大到小排序。对于 i,j(j>i)
    ,从小到大枚举 j,当 sum[i]−sum[j]已经大于或等于 P 时,更大的 j 肯定不能得到更大的答
    案。当 sum[队首]−sum[队尾]的值小于 P 时,在这个队列中满足差的绝对值最接近 P 的一
    对前缀和显然就是 sum[队首]和 sum[队尾]
    。这里显然满足单调队列模型。
    满足剩下的条件,注意细节即可。
     
    code:
    #include<stdio.h>
    #include<algorithm>
    #include<deque>
    #include<iostream>
    #define MAXN 100005
    using namespace std;
    
    int N,T,R,L,Ans,P,Delta;
    
    struct node{int id,v;}sum[MAXN];
    bool operator<(node x,node y){if(x.v==y.v)return x.id<y.id;return x.v>y.v;}
    
    void Solve()
    {
        Delta=L=R=1e9;
    
        deque<int>Q;
        int i,t,a,b,tmp;
    
        for(i=0;i<=N;i++)
        {
            while(Q.size()&&sum[Q.front()].v-sum[i].v>=P)
            {
                t=Q.front();
                tmp=sum[t].v-sum[i].v;
                a=min(sum[t].id,sum[i].id);
                b=max(sum[t].id,sum[i].id);
    
                if(tmp-P<=Delta)
                {
                    if(tmp-P==Delta)
                    {
                        if(L>=a)
                        {
                            if(L==a)R=min(R,b);
                            else L=a,R=b;
                        }
                    }
                    else
                    {
                        Ans=tmp;
                        L=a;R=b;
                    }
                    Delta=tmp-P;
                }
                Q.pop_front();
            }
            Q.push_back(i);
            t=Q.front();
            tmp=sum[t].v-sum[i].v;
            a=min(sum[t].id,sum[i].id);
            b=max(sum[t].id,sum[i].id);
            if(P-tmp<=Delta&&Q.size()!=1)
            {
                if(P-tmp==Delta)
                {
                    if(L>=a)
                    {
                        if(L==a)R=min(R,b);
                        else L=a,R=b;
                    }
                }
                else
                {
                    Ans=tmp;
                    L=a;R=b;
                }
                Delta=P-tmp;
            }
        }
    
        printf("%d %d %d
    ",Ans,L+1,R);
    }
    
    int main()
    {
        int i,x;
    
        scanf("%d%d",&N,&T);
        for(i=1;i<=N;i++)
        {
            scanf("%d",&x);
            sum[i].v=sum[i-1].v+x;
            sum[i].id=i;
        }
    
        sort(sum,sum+N+1);
    
        for(i=1;i<=T;i++)
        {
            scanf("%d",&P);
            Solve();
        }
    }
    刀剑映出了战士的心。而我的心,漆黑且残破
  • 相关阅读:
    2.7OpenIdConnectHandler 【RemoteAuthenticationHandler、IAuthenticationSignOutHandler】
    2.6OAuthHandler【RemoteAuthenticationHandler】
    2.0AuthenticationHandler【IAuthenticationHandler 】
    2.5RemoteAuthenticationHandler【AuthenticationHandler、IAuthenticationRequestHandler】
    2.4JwtBearerHandler 【AuthenticationHandler】
    在Centos7上安装Nominatim
    .net core使用 ELK
    linux 韩顺平课程笔记 3.11包管理工具(RPM和YUM)
    linux 韩顺平课程笔记 3.10进程管理
    linux 韩顺平课程笔记 3.5实用指令
  • 原文地址:https://www.cnblogs.com/OIEREDSION/p/11214594.html
Copyright © 2020-2023  润新知