鸡格线(牛客寒假训练营1)

文章介绍了两种方法解决一个涉及函数f(x)=10*sqrt(x)的问题,该函数在有限次操作后会收敛到一个不变值x0。第一种方法使用集合(set)来存储未达到x0的下标,并通过二分查找进行区间操作。第二种方法采用线段树来维护区间和最大值、最小值,通过比较最大值和最小值判断是否所有元素都达到x0,从而优化操作过程。
摘要由CSDN通过智能技术生成

 

 

分析: 通过观察可以看出f(x)经过不多的改变次数会收敛到一个不变的值,即f(x0)=x0,具体的为 0, 99 , 100 (打表等方法求出)。因为总操作次数不多,则可以得出一些总操作次数为复杂度的做法。

1:

set维护:set<int>q,q里面存未达到收敛值(x0)的下标,则每次对于 【l,r】区间,通过二分函数在q里面找到大于l且未达到收敛值的下标(id)进行操作,操作后若达到收敛值则从q里面删除该下标(id)。对于总和,可以用变量ans提前存上最初的和,然后对于每次操作后进行相应变化,具体看代码。总操作次数ser不大

#include<bits/stdc++.h>
using namespace std;
#define ll long long
set<int>q;//存未达到x0的下标
int a[100010];
int f(int x)
{
    return round(10*sqrt(x));
}
int main ()
{
    int n,m;
    cin>>n>>m;
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        ans+=a[i];//记录总和
        q.insert(i);//记录下标,初始时全部加入,也可用函数f(x)判断后再选择加入与否
    }
    q.insert(n+1);//加入n+1防空
    for(int i=1;i<=m;i++)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int l,r,k;
            cin>>l>>r>>k;
            int pos=l;
            while(1)
            {
                int id=*q.lower_bound(pos);//找到大于等于l且未达到x0的下标
                if(id>r)break;//如果id大于当前区间,结束循环
                for(int i=1;i<=k;i++)
                {
                    ans-=a[id];
                    a[id]=f(a[id]);
                    ans+=a[id];   //操作后更新ans
                    if(f(a[id])==a[id])break;//当达到x0后则不用继续操作了,直接结束循环
                }
                if(f(a[id])==a[id])q.erase(id);//如果达到x0,则从q里删除该下标
                pos=id+1;//下一次则从当前操作下标的下一个开始查找
            }
        }
        else cout<<ans<<"\n";
    }
    return 0;
}

总操2作次数不大

2:

线段树直接维护: 线段树节点维护区间sum,max,min,当一个区间都达到x0时既不用修改,判断方法可以为:max<=100&&min>=99,对于初始的0(其他数不会变为0)可直接让最值为99或100(不影响),然后一般线段树维护即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int a[100010];
struct no
{
    ll sum;
    int ma,mi;
    int l,r;
}t[400010];
void pushup(int x)
{
    t[x].ma=max(t[x<<1].ma,t[x<<1|1].ma);
    t[x].mi=min(t[x<<1].mi,t[x<<1|1].mi);
    t[x].sum=t[x<<1].sum+t[x<<1|1].sum;
}
void build(int x,int l,int r)
{
    t[x].l=l;
    t[x].r=r;
    if(l==r)
    {
        t[x].ma=t[x].mi=t[x].sum=a[l];
        if(a[l]==0)t[x].ma=t[x].mi=99;
        return ;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    pushup(x);
}
void modify(int x,int l,int r,int k)
{
    if(t[x].ma<=100&&t[x].mi>=99)return ;
    if(t[x].r<l||t[x].l>r)return ;
    if(t[x].l==t[x].r)
    {
        while(k&&t[x].sum!=100&&t[x].sum!=99)
        {
            t[x].sum=round(10*sqrt(t[x].sum));
            k--;
        }
        t[x].ma=t[x].mi=t[x].sum;
        return ;
    }
    modify(x<<1,l,r,k);
    modify(x<<1|1,l,r,k);
    pushup(x);
}
int main ()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int op;
        cin>>op;
        if(op==1)
        {
            int l,r,k;
            cin>>l>>r>>k;
            modify(1,l,r,k);
        }
        else cout<<t[1].sum<<"\n";
    }
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nj745

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值