线段树维护区间开方/除法

今天考试考了一些神仙数据结构

T1 线段树维护区间加,区间开方,区间和

(数据范围:5e5)

T2 线段树维护区间加,区间除,区间和,区间最值

对于这些题目,就像是之前考的区间与,区间或一样,除法,开方的操作会让各个数字之间越来越相近,最后变成一串一串连续的数字都是一样的,所以对于这一部分的操作我们一定程度上使用暴力,而如果一段都相等就相当于直接进行区间剪发的操作

那么我们来看如何判断区间一段都相等,那我们只需要维护区间的最值,最小值==最大值就完全相等了

然后.....代码.....被 \(yurzhang\) 神犇卡常,然后 \(huyufeifei\) 巨佬把我的 \(T1\) 改的面目全非......

为了大常数的尊严 ,还是放被卡常的代码

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define re register
#define gc getchar()
inline ll read()
{
    re char c(gc);re ll x(0);
    while(c<'0'||c>'9') c=gc;
    while(c>='0'&&c<='9') x=x*10+c-48,c=gc;
    return x;
}

const int N=5e5+10;

ll a[N],s[N<<2],minn[N<<2],maxx[N<<2],add[N<<2];

#define mid ((l+r)>>1)
#define ls id<<1
#define rs id<<1|1

inline void pushup(int id)
{
    s[id]=s[ls]+s[rs];
    minn[id]=min(minn[ls],minn[rs]);
    maxx[id]=max(maxx[ls],maxx[rs]);
}
inline void pushdown(int id,int l,int r)
{
    if(add[id]!=0)
    {
        re int lx=mid-l+1,rx=r-mid;
        add[ls]+=add[id];
        add[rs]+=add[id];
        s[ls]+=add[id]*lx;
        s[rs]+=add[id]*rx;
        minn[rs]+=add[id];
        minn[ls]+=add[id];
        maxx[ls]+=add[id];
        maxx[rs]+=add[id];
        add[id]=0;
    }
}
void built(int id,int l,int r)
{
    if(l==r)
    {
        s[id]=a[l];
        minn[id]=a[l];
        maxx[id]=a[l];
        return ;
    }
    built (ls,l,mid);
    built (rs,mid+1,r);
    pushup(id);
}
void change(int id,int l,int r,int L,int R,ll x)
{
    if(l>=L&&r<=R)
    {
        s[id]+=x*(r-l+1);
        add[id]+=x;
        minn[id]+=x;
        maxx[id]+=x;
        return;
    }
    pushdown(id,l,r);
    if(mid>=L) change(ls,l,mid,L,R,x);
    if(mid<R) change(rs,mid+1,r,L,R,x);
    pushup(id);
}
void Change(int id,int l,int r,int L,int R)
{
    if(l>=L&&r<=R&&floor(sqrt(maxx[id]))-maxx[id]==floor(sqrt(minn[id]))-minn[id])
    {
        ll d=floor(sqrt(maxx[id]))-maxx[id];
//      cout<<maxx[id]<<' '<<d<<endl;
        add[id]+=d;
        maxx[id]+=d;
        minn[id]+=d;
        s[id]+=d*(r-l+1);
        return;
    }
    pushdown(id,l,r);
    if(L<=mid) Change(ls,l,mid,L,R);
    if(mid<R) Change(rs,mid+1,r,L,R);
    pushup(id);
}
ll query(int id,int l,int r,int L,int R)
{
    if(l>=L&&r<=R) return s[id];
    pushdown(id,l,r);
    ll ans=0;
    if(L<=mid) ans+=query(ls,l,mid,L,R);
    if(R>mid) ans+=query(rs,mid+1,r,L,R);
    return ans;
}

int n,m;

int main()
{
    freopen("comp.in","r",stdin);
    freopen("comp.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;++i) a[i]=read();
    built(1,1,n);
    while(m--)
    {
        int op=read(),l=read(),r=read();
        if(op==1) {ll x=read();change(1,1,n,l,r,x);}
        if(op==2) {cout<<query(1,1,n,l,r)<<endl;}
        if(op==3) {Change(1,1,n,l,r);}
    }
    return 0;
}
#include <bits/stdc++.h>

using namespace std;

#define re register
#define gc getchar()
inline int read()
{
    re char c(gc);re int x(0);
    while(c<'0'||c>'9') c=gc;
    while(c>='0'&&c<='9') x=x*10+c-48,c=gc;
    return x;
}

#define ll long long
const int N=1e5+10;

ll a[N],s[N<<2],minn[N<<2],maxx[N<<2],add[N<<2];

#define mid ((l+r)>>1)
#define ls id<<1
#define rs id<<1|1

inline void pushup(int id)
{
    s[id]=s[ls]+s[rs];
    minn[id]=min(minn[ls],minn[rs]);
    maxx[id]=max(maxx[ls],maxx[rs]);
}
inline void pushdown(int id,int l,int r)
{
    if(add[id]!=0)
    {
        re int lx=mid-l+1,rx=r-mid;
        add[ls]+=add[id];
        add[rs]+=add[id];
        s[ls]+=add[id]*lx;
        s[rs]+=add[id]*rx;
        minn[rs]+=add[id];
        minn[ls]+=add[id];
        maxx[ls]+=add[id];
        maxx[rs]+=add[id];
        add[id]=0;
    }
}
void built(int id,int l,int r)
{
    if(l==r)
    {
        s[id]=a[l];
        minn[id]=a[l];
        maxx[id]=a[l];
        return ;
    }
    built (ls,l,mid);
    built (rs,mid+1,r);
    pushup(id);
}
void change(int id,int l,int r,int L,int R,ll x)
{
    if(l>=L&&r<=R)
    {
        s[id]+=x*(r-l+1);
        add[id]+=x;
        minn[id]+=x;
        maxx[id]+=x;
        return;
    }
    pushdown(id,l,r);
    if(mid>=L) change(ls,l,mid,L,R,x);
    if(mid<R) change(rs,mid+1,r,L,R,x);
    pushup(id);
}
void Change(int id,int l,int r,int L,int R,ll x)
{
    if (l >=L&&r<=R&&maxx[id]-floor(maxx[id]/x)==minn[id]-floor(minn[id]/x))
    {
        ll d=floor(maxx[id]/x)-maxx[id];
        add[id]+=d;
        maxx[id]+=d;
        minn[id]+=d;
        s[id]+=d*(r-l+1);
        return;
    }
    pushdown(id,l,r);
    if(L<=mid) Change(ls,l,mid,L,R,x);
    if(mid<R) Change(rs,mid+1,r,L,R,x);
    pushup(id);
}
ll query(int id,int l,int r,int L,int R)
{
    if(l>=L&&r<=R) return s[id];
    pushdown(id,l,r);
    ll ans=0;
    if(L<=mid) ans+=query(ls,l,mid,L,R);
    if(R>mid) ans+=query(rs,mid+1,r,L,R);
    return ans;
}
ll Query(int id,int l,int r,int L,int R)
{
    if(l>=L&&r<=R) return maxx[id];
    pushdown(id,l,r);
    ll ans=0;
    if(L<=mid) ans=max(ans,Query(ls,l,mid,L,R));
    if(R>mid) ans=max(ans,Query(rs,mid+1,r,L,R));
    return ans;
}

int n,m;

int main()
{
    freopen("count.in","r",stdin);
    freopen("count.out","w",stdout);
    n=read(),m=read();
    for(int i=1;i<=n;++i) a[i]=read();
    built(1,1,n);
    while(m--)
    {
        int op=read();
        ll l=read(),r=read();
        if(op==1) {int x=read();change(1,1,n,l,r,x);}
        if(op==2) {cout<<query(1,1,n,l,r)<<endl;}
        if(op==3) {cout<<Query(1,1,n,l,r)<<endl;}
        if(op==4) {int x=read();Change(1,1,n,l,r,x);}
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zijinjun/p/11047062.html

时间: 2024-10-25 05:33:46

线段树维护区间开方/除法的相关文章

线段树维护区间最大子段和

线段树:我还是很强的 简略讲解 要用线段树维护区间,我们要明确: 线段树存什么东西 怎么合并 如果有区间修改,怎么打标记 对于区间最大子段和,我们可以记录四个值:以维护的区间左端点为起点的最大子段和,以维护的区间右端点为终点的最大子段和,在维护区间内的最大子段和 和维护区间所有元素的和 合并的话稍微麻烦一些,看代码吧: inline void up(int p){ tree[p].sum=tree[ls].sum+tree[rs].sum; //维护区间总和 tree[p].ll=max(tre

最敏捷的机器人(线段树维护区间最值)

题面: Wind设计了很多机器人.但是它们都认为自己是最强的,于是,一场比赛开始了--机器人们都想知道谁是最敏捷的,于是它们进行了如下一个比赛.首先,他们面前会有一排共n个数,它们比赛看谁能最先把每连续k个数中最大和最小值写下来,当然,这些机器人运算速度都很,它们比赛的是谁写得快.但是Wind也想知道答案,你能帮助他吗? Input: 每组测试数据 第1行为n,k(1<=k<=n<=100000) 第2行共n个数,为数字序列,所有数字均在int范围内. Output: 共n-k+1行 第

线段树维护区间最大子段和 枚举 HDU6638

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6638 题意:在一个二维坐标系上给你n(n<=2000)个点,点带有一个价值w(有正有负),点的坐标都在(-1e9,1e9)的范围之间,可任意用一个平行于坐标轴的矩形框住一片区域,求这片区域框住的点的价值和 分析:点的坐标范围太大,离散化应能想到.离散化后可以考虑枚举左边界,枚举左边界后按照横坐标的依次加点(以一列一列为单位),用线段树维护一列的最大子段和,每移动到新的一列,继续加点时,等价于向原先的

【GDKOI2016Day1T1-魔卡少女】【拆位】线段树维护区间内所有连续子区间的异或和

题意:给出N个数,M个操作.操作有修改和询问两种,每次修改将一个数改成另一个数,每次询问一个区间的所有连续子区间的异或和.n,m<=100000,ai<=1000 题解: 当年(其实也就是今年)做不出来的题..D1T1啊... 因为ai<=1000,我们可以拆位处理.拆成10个二进制位,每位开1棵线段树. 对于每个节点,维护: d:这段区间的异或和 L[0],L[1]:子区间一定从左端点开始,异或和为0,1的子区间分别有多少个 R[0],R[1]:子区间一定从右端点开始,异或和为0,1的

HDU 2795 Billboard 【线段树维护区间最大值&amp;&amp;查询变形】

任意门:http://acm.hdu.edu.cn/showproblem.php?pid=2795 Billboard Time Limit: 20000/8000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 28743    Accepted Submission(s): 11651 Problem Description At the entrance to the un

线段树维护区间合并——cf1285E

感觉自己的解法又是歪的 代码写的很乱..要先找出一开始有多少段,然后计算删掉每条线段的贡献,求个最大值就可以 删每条线段的贡献可以用线段树区间合并来做 ps:正解其实很简单..扫描一下就可以了 /* 先把所有线段覆盖到线段树上 然后对每一个线段[L,R],查询区间[L,R]有多少值>1的段即可 */ #include<bits/stdc++.h> using namespace std; #define N 800005 int n,x[N],L[N],R[N],tot; #define

hdu 4553 约会安排 (两个线段树维护区间)

include include include include include include include include define ll long long define FOR(i,l,r) for(int i = l ; i <= r ;++i ) define inf 1<<30 define EPS (1e-9) define lson(p) (p<<1) define rson(p) (p<<1|1) using namespace std;

Can you answer these queries I SPOJ - GSS1 (线段树维护区间连续最大值/最大连续子段和)

You are given a sequence A[1], A[2], ..., A[N] . ( |A[i]| ≤ 15007 , 1 ≤ N ≤ 50000 ). A query is defined as follows: Query(x,y) = Max { a[i]+a[i+1]+...+a[j] ; x ≤ i ≤ j ≤ y }. Given M queries, your program must output the results of these queries. Inp

HDU5692 dfs + 线段树维护区间最大值

先附上题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5692 Problem Description 百度科技园内有n个零食机,零食机之间通过n−1条路相互连通.每个零食机都有一个值v,表示为小度熊提供零食的价值. 由于零食被频繁的消耗和补充,零食机的价值v会时常发生变化.小度熊只能从编号为0的零食机出发,并且每个零食机至多经过一次.另外,小度熊会对某个零食机的零食有所偏爱,要求路线上必须有那个零食机. 为小度熊规划一个路线,使得路线上的价值总和最