[JZOJ4438]K小数查询

本文探讨了一个利用分块技术解决数组操作问题的高效算法,包括区间加法和查询第k小元素的解决方案。

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

题目大意

一个数组a1...n,有q个操作。
操作分成2种:
1 l r x:将第l到第r个数全部加上x
2 l r k:询问第l个到第r个数中第k

1n,q80000
在任何时候,都有|ai|5×106


题目分析

分块大法好!!!
显然这题要使用分块大法。于是问题来了,怎么查询第k小呢?
使用分块大法,我们可以很方便地查询区间内比某个数小的数的个数(块内维护排序,查询时二分)。那么我们就可以二分答案,转化为判定性问题:是否有且仅有k1个数小于二分值,然后再使用上面的方法判定即可。
区间加对于完整的块直接打标记,否则暴力重构。
时间复杂度O(qlog2anlog2n)。时限7000ms我怕谁?


代码实现

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>

using namespace std;

int read()
{
    int x=0,f=1;
    char ch=getchar();
    while (!isdigit(ch))
    {
        if (ch=='-')
            f=-1;
        ch=getchar();
    }
    while (isdigit(ch))
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}

const int N=80050;
const int B=450;
const int A=5000000;

int tag[B],st[B],en[B];
int a[N],c[N];
int n,q,bs,s;

void build()
{
    s=trunc(sqrt(n))+1;
    bs=n/s;
    int p=1;
    for (int i=1;i<=bs;i++,p+=s)
    {
        st[i]=p,en[i]=p+s-1;
        for (int j=st[i];j<=en[i];j++)
            c[j]=a[j];
        sort(c+st[i],c+en[i]+1);
    }
    if (p<=n)
    {
        bs++;
        st[bs]=p,en[bs]=n;
        for (int j=st[bs];j<=en[bs];j++)
            c[j]=a[j];
        sort(c+st[bs],c+en[bs]+1);
    }
}

void rebuild(int x)
{
    for (int i=st[x];i<=en[x];i++)
        c[i]=a[i];
    sort(c+st[x],c+en[x]+1);
}

void change(int l,int r,int edit)
{
    int lb=(l-1)/s+1,rb=(r-1)/s+1;
    if (lb==rb)
    {
        for (int i=l;i<=r;i++)
            a[i]+=edit;
        rebuild(lb);
    }
    else
    {
        for (int i=l;i<=s*lb;i++)
            a[i]+=edit;
        rebuild(lb);
        for (int i=r;i>=s*(rb-1)+1;i--)
            a[i]+=edit;
        rebuild(rb);
        for (int i=lb+1;i<rb;i++)
            tag[i]+=edit;
    }
}

int count(int x,int y)
{
    int ret=st[x]-1,l=st[x],r=en[x],mid;
    while (l<=r)
    {
        mid=l+r>>1;
        if (c[mid]+tag[x]<y)
            ret=mid,l=mid+1;
        else
            r=mid-1;
    }
    return ret-st[x]+1;
}

int calc(int l,int r,int x)
{
    int lb=(l-1)/s+1,rb=(r-1)/s+1,ret=0;
    if (lb==rb)
    {
        for (int i=l;i<=r;i++)
            ret+=((a[i]+tag[lb])<x);
    }
    else
    {
        for (int i=l;i<=s*lb;i++)
            ret+=((a[i]+tag[lb])<x);
        for (int i=r;i>=s*(rb-1)+1;i--)
            ret+=((a[i]+tag[rb])<x);
        for (int i=lb+1;i<rb;i++)
            ret+=count(i,x);
    }
    return ret;
}

int query(int l,int r,int k)
{
    int ret=0,L=-A,R=A,mid;
    while (L<=R)
    {
        mid=L+R>>1;
        if (calc(l,r,mid)<k)
        {
            ret=mid;
            L=mid+1;
        }
        else
            R=mid-1;
    }
    return ret;
}

int main()
{
    freopen("gs.in","r",stdin);
    freopen("gs.out","w",stdout);
    n=read();
    for (int i=1;i<=n;i++)
        a[i]=read();
    build();
    q=read();
    for (int i=1,t,l,r,x;i<=q;i++)
    {
        t=read(),l=read(),r=read(),x=read();
        if (t==1)
            change(l,r,x);
        else
            printf("%d\n",query(l,r,x));
    }
    fclose(stdin);
    fclose(stdout);
    return 0;
}
<think>嗯,用户问的是在Oracle数据库查询中如何让数值保留三位小数。首先,我得回想一下Oracle里的数值处理函数。常用的有ROUND、TRUNC,还有格式化输出的时候可以用TO_CHAR。 首先,ROUND函数应该是四舍五入到指定位数。比如ROUND(column, 3)就能四舍五入到三位小数。不过要注意,如果原始数据的小数位数不够的话,可能会补零或者不显示,具体要看Oracle的处理方式。 然后是TRUNC函数,这个函数会直接截断到指定位数,不进行四舍五入。所以用户如果不需要四舍五入,而是直接舍去后面的位数,可以用TRUNC(column, 3)。 另外,用户可能需要格式化显示结果,这时候可以用TO_CHAR函数,指定格式模型,比如'999.999',这样数字会被格式化成三位小数。不过要注意,TO_CHAR可能会返回字符串类型,如果后续需要数值计算的话可能需要转换回来。 还要考虑到用户的需求场景。比如,如果只是查询时显示三位小数,但存储的数据不需要改变,那么用ROUND或者TO_CHAR都可以。如果需要在计算过程中保持三位精度,可能需要在插入或更新数据时使用精度设置,比如指定NUMBER(10,3)这样的列类型,这样数据库会自动处理精度和舍入问题。不过根据引用[1]里的内容,Oracle在输入超过精度时会报错,超过小数位数则会四舍五入,所以如果用户需要严格的三位小数存储,可能需要修改表结构或者约束。 另外,用户提到的是在查询时处理,所以重点应该放在查询语句的函数使用上。需要给出具体的例子,比如SELECT ROUND(salary, 3) FROM employees,或者SELECT TO_CHAR(salary, '999.999') FROM employees。同时要提醒用户注意数据类型的变化,尤其是TO_CHAR会转成字符串,可能影响后续处理。 还有可能用户不知道ROUND和TRUNC的区别,需要解释清楚两者的不同,避免误用。比如,如果数值是1.2345,ROUND到三位小数是1.235,而TRUNC则是1.234。 还要检查是否有其他相关函数或方法,比如CAST函数结合NUMBER类型,或者是否可以使用FORMAT参数,但根据引用[3],日期时间后缀只用于格式化输出,不能用于插入,所以数值方面可能类似,但不确定,需要确认。 总结下来,回答应该包括三种方法:ROUND、TRUNC、TO_CHAR,各自的使用场景和注意事项,并给出示例。同时可能需要提到表结构设计中对精度的控制,引用[1]的内容来强调输入时的完整性检查。</think>在Oracle数据库中实现数值保留三位小数,可通过以下三种方法实现: ### 一、使用ROUND函数(四舍五入) 语法示例: ```sql SELECT ROUND(column_name, 3) FROM table_name; ``` 此方法将数值四舍五入到小数点后第三位。例如: $$ 1.2345 \xrightarrow{ROUND} 1.235 $$ ### 二、使用TRUNC函数(直接截断) 语法示例: ```sql SELECT TRUNC(column_name, 3) FROM table_name; ``` 此方法直接截断超出三位的小数部分。例如: $$ 1.2349 \xrightarrow{TRUNC} 1.234 $$ ### 三、使用TO_CHAR格式化输出 语法示例: ```sql SELECT TO_CHAR(column_name, '999.999') FROM table_name; ``` 此方法将数值转换为字符串并强制显示三位小数(不足三位补零)。注意结果类型为字符串,例如: $$ 1.2 \xrightarrow{TO\_CHAR} \text{'  1.200'} $$ ### 精度控制建议 若需要永久存储三位小数,建议在表结构中定义字段类型: ```sql CREATE TABLE demo ( value NUMBER(10, 3) -- 总长度10位,含3位小数 ); ``` 根据Oracle的精度控制规则,输入值超过定义精度会触发错误,超过小数位会自动四舍五入[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值