CodeForces 438D 浅谈区间取模线段树

CodeForces 438D 浅谈区间取模线段树_第1张图片
世界真的很大
很多正解其实本来都是暴力,但是莫名其妙地过了,然后分析一波复杂度貌似没有问题,然后就是正解了
线段树的运用的却有很多,其实只要要处理的问题满足区间合并的性质就行了
在遇见新的需要处理的问题时,要优先考虑他的信息对于询问而言能不能区间合并
如果可以那就可以区间合并了
如果不行的话就只能想其他办法了,或者暴力上。
有些时候暴力并不一定不是正解,要好好分析题目性质,计算一波复杂度
看题先:
description:

长度为n的非负整数数列,3种操作
1. 求[L,R]所有数的和。
2. 将[L,R]中所有数都mod x。
3. 将a[i]修改为v。
n,m≤100000

input

第一行两个整数n,m,表示数列元素个数和操作数
接下来n个数,表示序列
接下来m行,每行开头一个整数表示操作

output

对于每一个询问操作,输出一个整数表示答案

如果没有2操作的话,还是很简单的线段树裸题,单点修改,区间查询
现在考虑怎么处理这个取模的问题
对于一段区间,如果取模的数比这段区间所有的数都大,那取模就是没有意义的,就是说,如果取模的数比区间最大的数还大,那么就不用取模了,所以我们在线段树里再记录一个区间最大值
考虑每次取模,对于每一个数x,取模y,x mod y的值必然比y小,如果y小于x/2,那x就变得小于x/2,如果y大于x/2,x剩下的部分也比x/2少,x也会变得比x/2小
那么就是说x每次取模都会变得比x/2小,就是说,对于一个数x,有效的取mod最多进行logx次,一共只会进行nlogn次取模,那么就算对所有的数取模,这个时间复杂度都是可以接受的
分析一波之后,发现,直接暴力去取模是可行的。
那么我们对于每个区间记录一个最大值,如果取模的数大于最大值,就不管,如果小于最大值,就暴力取模
完整代码:

#include
#include
using namespace std;
typedef long long dnt;

struct node
{
    dnt sum,big;
    node *ls,*rs;
    void update()
    {
        sum=ls->sum+rs->sum;
        big=max(ls->big,rs->big);
    }
}pool[2000010],*tail=pool,*root,*null;

dnt a[100010];
int n,m;

void init()
{
    null=++tail;
    null->sum=null->big=0;
    null->ls=null->rs=null;
}

node *newnode()
{
    node *nd=++tail;
    nd->ls=nd->rs=null;
    nd->sum=nd->big=0;
    return nd;
}

node *build(int lf,int rg)
{
    node *nd=newnode();
    if(lf==rg)
    {
        nd->big=nd->sum=a[lf];
        return nd;
    }
    int mid=(lf+rg)>>1;
    nd->ls=build(lf,mid);
    nd->rs=build(mid+1,rg);
    nd->update();
    return nd;
}

void modify(node *nd,int lf,int rg,int pos,dnt delta)
{
    if(lf==rg)
    {
        nd->sum=nd->big=delta;
        return ;
    }
    int mid=(lf+rg)>>1;
    if(pos<=mid) modify(nd->ls,lf,mid,pos,delta);
    else modify(nd->rs,mid+1,rg,pos,delta);
    nd->update();
}

void modifz(node *nd,int lf,int rg,int L,int R,dnt delta)
{
    if(nd->bigreturn ;
    if(lf==rg)
    {
        nd->sum%=delta,nd->big%=delta;
        return ;
    }
    int mid=(lf+rg)>>1;
    if(L<=mid) modifz(nd->ls,lf,mid,L,R,delta);
    if(R>mid) modifz(nd->rs,mid+1,rg,L,R,delta);
    nd->update();
}

dnt query(node *nd,int lf,int rg,int L,int R)
{
    if(L<=lf&&rg<=R)
        return nd->sum;
    int mid=(lf+rg)>>1;
    dnt rt=0;
    if(L<=mid) rt+=query(nd->ls,lf,mid,L,R);
    if(R>mid) rt+=query(nd->rs,mid+1,rg,L,R);
    return rt;
}

int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&a[i]);
    root=build(1,n);
    while(m--)
    {
        int opt,x,y;
        dnt z;
        scanf("%d",&opt);
        if(opt==3)
        {
            scanf("%d%lld",&x,&z);
            modify(root,1,n,x,z);
        }
        else if(opt==2)
        {
            scanf("%d%d%lld",&x,&y,&z);
            modifz(root,1,n,x,y,z);
        }
        else if(opt==1)
        {
            scanf("%d%d",&x,&y);
            printf("%lld\n",query(root,1,n,x,y));
        }
    }
    return 0;
}
/*
Whoso pulleth out this sword from this stone and anvil is duly born King of all England
*/

嗯,就是这样

你可能感兴趣的:(线段树,codeforces)