线段树进阶

T1:高速公路

题干:

  $Y901$ 高速公路是一条重要的交通纽带,政府部门建设初期的投入以及使用期间的养护费用都不低,因此政府在这条高速公路上设立了许多收费站。

  $Y901$ 高速公路是一条由 $N-1$ 段路以及 $N$ 个收费站组成的东西向的链,我们按照由西向东的顺序将收费站依次编号为1~N,从收费站 i 行驶到 i+1 (或从 i+1 行驶到 i )需要收取 V_i 的费用。高速路刚建成时所有的路段都是免费的。

  政府部门根据实际情况,会不定期地对连续路段的收费标准进行调整,根据政策涨价或降价。

  无聊的小 A 同学总喜欢研究一些稀奇古怪的问题,他开车在这条高速路上行驶时想到了这样一个问题:对于给定的 l , r ( l < r ),在第 l 个到第 r 个收费站里等概率随机取出两个不同的收费站 a 和 b ,那么从 a 行驶到 b 将期望花费多少费用呢?

  第一行 2 个正整数 N , M ,表示有 N 个收费站, M 次调整或询问。接下来 M 行,每行将出现以下两种形式中的一种:

  C l r v 表示将第 l 个收费站到第 r 个收费站之间的所有道路的通行费全部增加 v

  Q l r 表示对于给定的 l , r ,要求回答小 A 的问题

  所有 C 与 Q 操作中保证 1<= l < r <= N

题解:

  这道题就是一个维护区间修改的线段树优化。我们发现权值是在边上,我们比较容易地就可以想到将边标号,用线段树维护边的权值。

  我们考虑一下每一条小边的贡献:

$ans=\sum\limits_{i=l}^r a[i]*(r-i+1)*(i-l+1)$

  (因为一旦一条路线的左右边界在这条小边的两侧,这条小边就会做贡献,所以就有后面的两个常数)

  化简后可得:

$ans=(r−l+1−r*l)*sum_1+(r+l)*sum_2−sum_3$

$sum_1=\sum\limits_{i=l}^r a[i]$

$sum_2=\sum\limits_{i=1}^r a[i]*i$

$sum_3=\sum\limits_{i=1}^r a[i]*i^2$

  线段树维护 sum_1、sum_2、sum_3 三个懒标记即可。

Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #define $ 100010
 4 #define ll long long
 5 using namespace std;
 6 ll m,n,ans1,ans2,gcd;
 7 struct tree{    int l,r; ll i,i1,i2,lazy;    }a[$*4];
 8 inline void pushup(int x,ll val){
 9     ll l=a[x].l, r=a[x].r;
10     a[x].lazy+=val;
11     a[x].i+=val*(r-l+1);
12     a[x].i1+=val*(r-l+1)*(l+r)/2;
13     a[x].i2+=val*(1ll*r*(r+1)*(2*r+1)-1ll*l*(l-1)*(2*l-1))/6;
14 }
15 inline void pushdown(int x){
16     if(a[x].lazy==0) return ;
17     pushup(x<<1,a[x].lazy);  pushup(x<<1|1,a[x].lazy);
18     a[x].lazy=0;
19 }
20 inline void build(int x,int l,int r){
21     a[x].l=l;  a[x].r=r;
22     if(a[x].l==a[x].r) return;
23     int mid=(a[x].l+a[x].r)>>1;
24     build(x<<1,l,mid);  build(x<<1|1,mid+1,r);
25 }
26 inline void change(int x,int l,int r,ll val){
27     if(l<=a[x].l&&a[x].r<=r){    pushup(x,val); return;    }
28     pushdown(x);
29     int mid=(a[x].l+a[x].r)>>1;
30     if(l<=mid) change(x<<1,l,r,val);
31     if(mid<r)  change(x<<1|1,l,r,val);
32     a[x].i=a[x<<1].i+a[x<<1|1].i;
33     a[x].i1=a[x<<1].i1+a[x<<1|1].i1;
34     a[x].i2=a[x<<1].i2+a[x<<1|1].i2;
35 }
36 inline ll query(int x,int l,int r,int opt,ll ans=0){
37     if(l<=a[x].l&&a[x].r<=r){
38         if(opt==0) return a[x].i;
39         if(opt==1) return a[x].i1;
40         if(opt==2) return a[x].i2;
41     }
42     pushdown(x);
43     int mid=(a[x].l+a[x].r)>>1;
44     if(l<=mid) ans+=query(x<<1,l,r,opt);
45     if(mid<r)  ans+=query(x<<1|1,l,r,opt);
46     return ans;
47 }
48 inline ll Gcd(ll a,ll b){    return (b==0)?a:Gcd(b,a%b);    }
49 signed main(){
50     scanf("%lld%lld",&n,&m); build(1,1,n-1);
51     for(register int i=1,l,r,v;i<=m;++i){
52         char s=getchar();
53         while(s!=‘C‘&&s!=‘Q‘) s=getchar();
54         if(s==‘C‘) scanf("%d%d%d",&l,&r,&v), change(1,l,r-1,1ll*v);
55         if(s==‘Q‘){
56             scanf("%d%d",&l,&r);
57             ans1=query(1,l,r-1,1)*(l+r-1)-query(1,l,r-1,0)*r*(l-1)-query(1,l,r-1,2);
58             ans2=1ll*(r-l+1)*(r-l)/2;
59             gcd=Gcd(ans1,ans2);
60             printf("%lld/%lld\n",ans1/gcd,ans2/gcd);
61         }
62     }
63 }

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<vector>
 7 #include<queue>
 8 using namespace std;
 9 #define int long long
10 const int N=1e5+10;
11 struct node{
12     int l,r,s1,s2,s3,s4,s5,lazy;//i^2*s[i],i*s[i],s[i];
13 }tr[N<<2];
14 int ans1,ans2,ans3;
15 void down(int p){
16     tr[p<<1].s1+=(tr[p<<1].r-tr[p<<1].l+1)*tr[p].lazy;
17     tr[p<<1|1].s1+=(tr[p<<1|1].r-tr[p<<1|1].l+1)*tr[p].lazy;
18     tr[p<<1].s2+=tr[p<<1].s4*tr[p].lazy;
19     tr[p<<1|1].s2+=tr[p<<1|1].s4*tr[p].lazy;
20     tr[p<<1].s3+=tr[p<<1].s5*tr[p].lazy;
21     tr[p<<1|1].s3+=tr[p<<1|1].s5*tr[p].lazy;
22     tr[p<<1].lazy+=tr[p].lazy;
23     tr[p<<1|1].lazy+=tr[p].lazy;
24     tr[p].lazy=0;
25 }
26 void build(int p,int l,int r){
27     tr[p].l=l,tr[p].r=r;
28     if(l==r){
29         tr[p].s4=l;
30         tr[p].s5=l*l;
31         return ;
32     }
33     int mid=(l+r)>>1;
34     build(p<<1,l,mid);
35     build(p<<1|1,mid+1,r);
36     tr[p].s4=tr[p<<1].s4+tr[p<<1|1].s4;
37     tr[p].s5=tr[p<<1].s5+tr[p<<1|1].s5;
38 }
39 void add(int l,int r,int val,int p){
40     if(tr[p].l>=l&&tr[p].r<=r){
41         tr[p].lazy+=val;
42         tr[p].s1+=(tr[p].r-tr[p].l+1)*val;
43         tr[p].s2+=tr[p].s4*val;
44         tr[p].s3+=tr[p].s5*val;
45         return ;
46     }
47     down(p);
48     int mid=(tr[p].l+tr[p].r)>>1;
49     if(l<=mid)add(l,r,val,p<<1);
50     if(r>mid) add(l,r,val,p<<1|1);
51     tr[p].s1=tr[p<<1].s1+tr[p<<1|1].s1;
52     tr[p].s2=tr[p<<1].s2+tr[p<<1|1].s2;
53     tr[p].s3=tr[p<<1].s3+tr[p<<1|1].s3;
54 }
55 void query(int p,int l,int r){
56     if(l<=tr[p].l&&tr[p].r<=r){
57         ans1+=tr[p].s1;
58         ans2+=tr[p].s2;
59         ans3+=tr[p].s3;
60         return ;
61     }
62     down(p);
63     int mid=(tr[p].l+tr[p].r)>>1;
64     if(l<=mid) query(p<<1,l,r);
65     if(r>mid) query(p<<1|1,l,r);
66 }
67 int gcd(int a,int b) {return b?gcd(b,a%b):a;}
68 signed main(){
69     int n,m;
70     scanf("%lld%lld",&n,&m);
71     build(1,1,n-1);
72     for(int i=1;i<=m;i++){
73         char ch[2];int l,r;
74         scanf("%s",ch);
75         if(ch[0]==‘C‘){
76             int val;
77             scanf("%lld%lld%lld",&l,&r,&val);
78             add(l,r-1,val,1);
79         }
80         else{
81             scanf("%lld%lld",&l,&r);
82             ans1=ans2=ans3=0;
83             query(1,l,--r);
84             int ans=-ans3+(l+r)*ans2+(r-l+1-l*r)*ans1;
85             ans*=2;
86             int res=(r-l+1)*(r-l+2);
87             int GCD=gcd(res,ans);
88             //ans/=GCD,res/=GCD;
89             printf("%lld/%lld\n",ans/GCD,res/GCD);
90         }
91     }
92 }

T2:CPU 监控

题干:

  Bob 需要一个程序来监视 CPU 使用率。这是一个很繁琐的过程,为了让问题更加简单,Bob 会慢慢列出今天会在用计算机时做什么事。 Bob 会干很多事,除了跑暴力程序看视频之外,还会做出去玩玩和用鼠标乱点之类的事,甚至会一脚踢掉电源……这些事有的会让做这件事的这段时间内 CPU 使用率增加或减少一个值;有的事还会直接让 CPU 使用率变为一个值。

  当然 Bob 会询问:在之前给出的事件影响下, CPU 在某段时间内,使用率最高是多少。有时候 Bob 还会好奇地询问,在某段时间内 CPU 曾经的最高使用率是多少。 为了使计算精确,使用率不用百分比而用一个整数表示。

  不保证 Bob 的事件列表出了莫名的问题,使得使用率为负………………

  输入格式:第一行一个正整数 T ,表示 Bob 需要监视 CPU 的总时间。

    然后第二行给出 T 个数表示在你的监视程序执行之前, Bob 干的事让 CPU 在这段时间内每个时刻的使用率达已经达到了多少。

    第三行给出一个数 E,表示 Bob 需要做的事和询问的总数。

    接下来E行每行表示给出一个询问或者列出一条事件:

    Q X Y : 询问从 X 到 Y 这段时间内CPU最高使用率

    A X Y : 询问从 X 到 Y 这段时间内之前列出的事件使 CPU 达到过的最高使用率

    P X Y Z : 列出一个事件这个事件使得从X到Y这段时间内 CPU 使用率增加 Z

    C X Y Z : 列出一个事件这个事件使得从X到Y这段时间内 CPU 使用率变为 Z  

    时间的单位为秒,使用率没有单位。

    X 和 Y 均为正整数(X <= Y),Z 为一个整数。

    从 X 到 Y 这段时间包含第 X 秒和第 Y 秒。

    保证必要运算在有符号 32 位整数以内。

题解:

  这道题真的十分难想,当时想到了一个十分难实现的懒标记,还是错的。。。(懒标记需要有优先级,否则在寻找历史最大值与现有最大值时会算不全,懒标记的延迟十分难搞)多亏有学长的 ppt ,才顿悟打了出来。来解释一下:

  这道题只需要 6 个懒标记,有 历史最大值、现有最大值、历史最大覆盖值、现有覆盖值、

Code:

  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<algorithm>
  5 #define $ 100010
  6 #define int long long
  7 #define inf 0x7ffffffffff
  8 using namespace std;
  9 int m,n,k,t,w[$],e;
 10 struct tree{    int l,r,max,hmax,add,hadd,set,hset;    }a[$*4];
 11 inline int max(int x,int y){    return x>y?x:y;    }
 12 inline int min(int x,int y){    return x<y?x:y;    }
 13 inline void pushdown(int x){
 14     a[x<<1].hmax=max(a[x<<1].hmax,a[x<<1].max+a[x].hadd);
 15     if(a[x<<1].hset!=-inf)
 16         a[x<<1].hset=max(a[x<<1].hset,a[x].hadd+a[x<<1].set);
 17     else
 18         a[x<<1].hadd=max(a[x<<1].hadd,a[x].hadd+a[x<<1].add);
 19     a[x<<1].hmax=max(a[x<<1].hmax,a[x].hset);
 20     a[x<<1].hset=max(a[x<<1].hset,a[x].hset);
 21     if(a[x].add!=0){
 22         a[x<<1].max+=a[x].add;
 23         a[x<<1].hmax=max(a[x<<1].hmax,a[x<<1].max);
 24         if(a[x<<1].set==-inf)
 25             a[x<<1].add+=a[x].add, a[x<<1].hadd=max(a[x<<1].hadd,a[x<<1].add);
 26         else
 27             a[x<<1].set+=a[x].add, a[x<<1].hset=max(a[x<<1].hset,a[x<<1].set);
 28     }
 29     else if(a[x].set!=-inf){
 30         a[x<<1].max=a[x].set;
 31         a[x<<1].hmax=max(a[x<<1].hmax,a[x<<1].max);
 32         a[x<<1].set=a[x].set;
 33         a[x<<1].hset=max(a[x<<1].hset,a[x].set);
 34         a[x<<1].add=0;
 35     }
 36
 37     a[x<<1|1].hmax=max(a[x<<1|1].hmax,a[x<<1|1].max+a[x].hadd);
 38     if(a[x<<1|1].hset!=-inf)
 39         a[x<<1|1].hset=max(a[x<<1|1].hset,a[x].hadd+a[x<<1|1].set);
 40     else
 41         a[x<<1|1].hadd=max(a[x<<1|1].hadd,a[x].hadd+a[x<<1|1].add);
 42     a[x<<1|1].hmax=max(a[x<<1|1].hmax,a[x].hset);
 43     a[x<<1|1].hset=max(a[x<<1|1].hset,a[x].hset);
 44     if(a[x].add!=0){
 45         a[x<<1|1].max+=a[x].add;
 46         a[x<<1|1].hmax=max(a[x<<1|1].hmax,a[x<<1|1].max);
 47         if(a[x<<1|1].set==-inf)
 48             a[x<<1|1].add+=a[x].add, a[x<<1|1].hadd=max(a[x<<1|1].hadd,a[x<<1|1].add);
 49         else
 50             a[x<<1|1].set+=a[x].add, a[x<<1|1].hset=max(a[x<<1|1].hset,a[x<<1|1].set);
 51     }
 52     else if(a[x].set!=-inf){
 53         a[x<<1|1].max=a[x].set;
 54         a[x<<1|1].hmax=max(a[x<<1|1].hmax,a[x<<1|1].max);
 55         a[x<<1|1].set=a[x].set;
 56         a[x<<1|1].hset=max(a[x<<1|1].hset,a[x].set);
 57         a[x<<1|1].add=0;
 58     }
 59
 60     a[x].set=a[x].hset=-inf;
 61     a[x].add=a[x].hadd=0;
 62 }
 63 inline void build(int x,int l,int r){
 64     a[x].l=l;  a[x].r=r;
 65     a[x].max=a[x].hmax=a[x].set=a[x].hset=-inf;
 66     a[x].add=a[x].hadd=0;
 67     if(a[x].l==a[x].r){    a[x].max=a[x].hmax=w[l]; return;    }
 68     int mid=(a[x].l+a[x].r)>>1;
 69     build(x<<1,l,mid);  build(x<<1|1,mid+1,r);
 70     a[x].max=max(a[x<<1].max,a[x<<1|1].max);
 71     a[x].hmax=max(a[x<<1].hmax,a[x<<1|1].hmax);
 72 }
 73 inline void add(int x,int l,int r,int val){
 74     if(l<=a[x].l&&a[x].r<=r){
 75         a[x].max+=val;
 76         a[x].hmax=max(a[x].hmax,a[x].max);
 77         if(a[x].set==-inf) a[x].add+=val, a[x].hadd=max(a[x].hadd,a[x].add);
 78         else               a[x].set+=val, a[x].hset=max(a[x].hset,a[x].set);
 79         return;
 80     }
 81     pushdown(x);
 82     int mid=(a[x].l+a[x].r)>>1;
 83     if(l<=mid)  add(x<<1,l,r,val);
 84     if(mid<r)   add(x<<1|1,l,r,val);
 85     a[x].max=max(a[x<<1].max,a[x<<1|1].max);
 86     a[x].hmax=max(a[x<<1].hmax,a[x<<1|1].hmax);
 87 }
 88 inline void change(int x,int l,int r,int val){
 89     if(l<=a[x].l&&a[x].r<=r){
 90         a[x].max=a[x].set=val;
 91         a[x].hmax=max(a[x].max,a[x].hmax);
 92         a[x].hset=max(val,a[x].hset);
 93         a[x].add=0;
 94         return;
 95     }
 96     pushdown(x);
 97     int mid=(a[x].l+a[x].r)>>1;
 98     if(l<=mid)  change(x<<1,l,r,val);
 99     if(mid<r)   change(x<<1|1,l,r,val);
100     a[x].max=max(a[x<<1].max,a[x<<1|1].max);
101     a[x].hmax=max(a[x<<1].hmax,a[x<<1|1].hmax);
102 }
103 inline int ask_max(int x,int l,int r,int ans=-inf){
104     if(l<=a[x].l&&a[x].r<=r)  return a[x].max;
105     pushdown(x);
106     int mid=(a[x].l+a[x].r)>>1;
107     if(l<=mid) ans=max(ans,ask_max(x<<1,l,r));
108     if(mid<r)  ans=max(ans,ask_max(x<<1|1,l,r));
109     return ans;
110 }
111 inline int ask_hmax(int x,int l,int r,int ans=-inf){
112     if(l<=a[x].l&&a[x].r<=r)  return a[x].hmax;
113     pushdown(x);
114     int mid=(a[x].l+a[x].r)>>1;
115     if(l<=mid) ans=max(ans,ask_hmax(x<<1,l,r));
116     if(mid<r)  ans=max(ans,ask_hmax(x<<1|1,l,r));
117     return ans;
118 }
119 signed main(){
120     scanf("%lld",&t);
121     for(register int i=1;i<=t;++i) scanf("%lld",&w[i]);
122     build(1,1,t);
123     scanf("%lld",&e);
124     for(register int i=1,x,y,z;i<=e;++i){
125         char s=getchar();
126         while(s!=‘Q‘&&s!=‘A‘&&s!=‘P‘&&s!=‘C‘) s=getchar();
127         if(s==‘Q‘) scanf("%lld%lld",&x,&y), printf("%lld\n",ask_max(1,x,y));
128         if(s==‘A‘) scanf("%lld%lld",&x,&y), printf("%lld\n",ask_hmax(1,x,y));
129         if(s==‘P‘) scanf("%lld%lld%lld",&x,&y,&z), add(1,x,y,z);
130         if(s==‘C‘) scanf("%lld%lld%lld",&x,&y,&z), change(1,x,y,z);
131     }
132 }

T3:

题干:

  在 2016 年,佳媛姐姐喜欢上了数字序列。因而他经常研究关于序列的一些奇奇怪怪的问题,现在他在研究一个难题,需要你来帮助他。这个难题是这样子的:给出一个 1 到 n 的全排列,现在对这个全排列序列进行 m 次局部排序,排序分为两种:

  1: ( 0 , l , r ) 表示将区间 [ l , r ] 的数字升序排序

  2 :( 1 , l , r ) 表示将区间 [ l , r ] 的数字降序排序最后询问第 q 位置上的数字。

  输入格式:输入数据的第一行为两个整数 n 和 m 。 n 表示序列的长度,m 表示局部排序的次数。1 <= n, m <= 10^5;第二行为 n 个整数,表示 1 到 n 的一个全排列。

    接下来输入 m 行,每一行有三个整数 op , l ,   r ,  op 为 0 代表升序排序, op 为 1 代表降序排序,  l , r  表示排序的区间。

    最后输入一个整数 q,q 表示排序完之后询问的位置, 1 <= q <= n。1 <= n <= 10^5,1 <= m <= 10^5

  输出格式:输出数据仅有一行,一个整数,表示按照顺序将全部的部分排序结束后第 q 位置上的数字。

题解:

  这道题想都没想就打了一个复杂度为 $\Theta(nmlog_2n)$ 的线段树优化桶排。。。(比快排还慢。。。)

  题中说明了一定是一个全排列,桶排就给跪了。。。全排列代表没有重复的数,这就有答案单调性——二分答案。

  我们可以二分

  时间复杂度:$\Theta(mlog_2nlog_2ans)$

Code:

 1 #include<cstdio>
 2 #include<cstring>
 3 #define $ 100100
 4 using namespace std;
 5 int m,n,k,t,sum[$],ans[$],tot,a[$],q,lp[$],rp[$],opt[$],minn,maxx,mid;
 6 struct tree{    int val,l,r,lazy;    }tr[$*4];
 7 inline void pushup(int x){
 8     tr[x].val=tr[x<<1].val+tr[x<<1|1].val;
 9 }
10 inline void pushdown(int x){
11     if(tr[x].lazy==-1) return;
12     tr[x<<1].lazy=tr[x<<1|1].lazy=tr[x].lazy;
13     tr[x<<1].val=(tr[x<<1].r-tr[x<<1].l+1)*tr[x].lazy;
14     tr[x<<1|1].val=(tr[x<<1|1].r-tr[x<<1|1].l+1)*tr[x].lazy;
15     tr[x].lazy=-1;
16 }
17 inline void build(int x,int l,int r){
18     tr[x].l=l, tr[x].r=r; tr[x].lazy=-1;
19     if(l==r) return;
20     int mid=(tr[x].l+tr[x].r)>>1;
21     build(x<<1,l,mid), build(x<<1|1,mid+1,r);
22 }
23 inline void change(int x,int l,int r,int val){
24     if(l<=tr[x].l&&tr[x].r<=r){
25         tr[x].val=(tr[x].r-tr[x].l+1)*val;
26         tr[x].lazy=val;
27         return ;
28     }
29     pushdown(x);
30     int mid=(tr[x].l+tr[x].r)>>1;
31     if(l<=mid)   change(x<<1,l,r,val);
32     if(mid+1<=r) change(x<<1|1,l,r,val);
33     pushup(x);
34 }
35 inline void pre(int x,int l,int r){
36     if(l<=tr[x].l&&tr[x].r<=r&&tr[x].val){
37         sum[tr[x].val]+=tr[x].r-tr[x].l+1;return;
38     }
39     pushdown(x);
40     int mid=(tr[x].l+tr[x].r)>>1;
41     if(l<=mid)   pre(x<<1,l,r);
42     if(mid+1<=r) pre(x<<1|1,l,r);
43     pushup(x);
44 }
45 inline int query(int x,int l,int r,int ans=0){
46     if(l<=tr[x].l&&tr[x].r<=r) return tr[x].val;
47     int mid=(tr[x].l+tr[x].r)>>1;
48     pushdown(x);
49     if(l<=mid)   ans+=query(x<<1,l,r);
50     if(mid+1<=r) ans+=query(x<<1|1,l,r);
51     return ans;
52 }
53 inline void work(int optt,int l,int r){
54     int tmp=query(1,l,r);
55     change(1,l,r,0);
56     if(!optt) change(1,r-tmp+1,r,1);
57     if(optt)  change(1,l,l+tmp-1,1);
58 }
59 inline bool judge(int x){
60     change(1,1,n,0);  change(1,mid+1,n,1);
61     for(register int i=1;i<=m;++i) work(opt[i],lp[i],rp[i]);
62     return query(1,q,q);
63 }
64 signed main(){
65     scanf("%d%d",&n,&m);  build(1,1,n);
66     for(register int i=1;i<=n;++i) scanf("%d",&a[i]);
67     for(register int i=1;i<=m;++i) scanf("%d%d%d",&opt[i],&lp[i],&rp[i]);
68     scanf("%d",&q);
69     minn=1, maxx=n;
70     while(minn<maxx){
71         mid=(minn+maxx)>>1;
72         if(judge(mid)) minn=mid+1;
73         else           maxx=mid;
74     }
75     printf("%d\n",minn);
76 }

T4:

题干:

题解:

Code:

原文地址:https://www.cnblogs.com/OI-zzyy/p/11291364.html

时间: 2024-10-10 05:59:06

线段树进阶的相关文章

线段树-进阶

上一次我们写的线段树已经可以解决区间查询.单点修改了!可喜可贺 那如果现在我们需要区间修改.区间查询呢? 一般有两种思路:lazytag和标记永久化,lazytag的使用面好像更广一些. 一.lazytag 比如我们现在要修改一个区间,我们可以像查询一样分成若干段,然后分别修改每一段. 那么问题来了,每一段要怎么修改?如果直接修改sum,询问就乱套了.如果暴力修改,最坏复杂度O(n),那还要线段树干嘛(╯‵□′)╯掀桌 那我们这么想,我们修改就不修改整个区间了,而是在区间上维护一个标记. 那我们

CSU-ACM集训-模板-线段树进阶

A题 原CF 438D The Child and Sequence 题意 给一串数字,m次操作,1.区间查询:2.区间取模:3.单点修改 基本思路 考虑到模如果大于区间的最大值,则取模没有意义.若小于则向下查询并修改,考虑到一个数每次取模最多为原数的\(1/2\),则可认为修改次数不超过\(\log{2}{n}\) 时间复杂度为\(O(n\log{2}{n}\log{2}{n})\) #include<bits/stdc++.h> #define FOR(i,a,b) for(int i=a

线段树进阶之落花成泥

————————————————————————相识,是在那么不经意的瞬间.我还远远不够,远远,远远,远远不够啊.加油呀! foundation : 1.异或,英文为exclusive OR,缩写成xor. 异或(xor)是一个数学运算符.它应用于逻辑运算.异或的数学符号为"⊕",计算机符号为"xor".其运算法则为:a⊕b = (¬a ∧ b) ∨ (a ∧¬b). 如果a.b两个值不相同,则异或结果为1.如果a.b两个值相同,异或结果为0. 异或也叫半加运算,其

线段树与树状数组草稿

http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=604&pid=1002 Dylans loves sequence Accepts: 249 Submissions: 806 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others) Problem Description Dylans is given N

线段树--从入门到精通

线段树,强大的数据结构,用处也是比较广的. 首先,我们要明白线段树是个啥? 线段树,线段嘛,有左右端点,那么它当然可以代表一个区间,那么区间上的好多事情都可以用它来搞,比如:区间加,区间乘,区间求和. 首先让我们先看个线段树的模型. 如图,这就是一棵线段树的模型. 圈内的点表示这是第几个点,红色表示这个点表示的区间范围. 每个点和它的左右两个儿子的编号是有一定的关系的: 点N,它的左儿子编号为N$\times$2,右儿子编号为N$\times$2+1. 线段树支持单点修改,区间修改,单点查询,区

「模板」线段树静态开点(单点+区间修改)、动态开点

相关讲解资料: 树状数组:https://blog.csdn.net/qq_34374664/article/details/52787481 (线段树预备) 线段树讲解: 初学版:https://blog.csdn.net/zearot/article/details/52280189 进阶完整版:https://www.cnblogs.com/AC-King/p/7789013.html 代码: 完整注释模板一张,参(chao)考(xi)楼上的博客 #include<iostream> #

超全面的线段树:从入门到入坟

超全面的线段树:从入门到入坟 \(Pre\):其实线段树已经学了很久了,突然线段树这个数据结构比较重要吧,现在想写篇全面的总结,帮助自己复习,同时造福广大\(Oier\)(虽然线段树的思维难度并不高).本篇立志做一篇最浅显易懂,最全面的线段树讲解,采用\(lyd\)写的<算法竞赛进阶指南>上的顺序,从最基础的线段树到主席树,本篇均会涉及,并且附有一定量的习题,以后可能会持续更新,那么现在开始吧! 目录 更新日志 线段树想\(AC\)之基本原理(雾*1 线段树想偷懒之懒标记(雾*2 线段树想应用

浅谈二维线段树的几种不同的写法

目录 参考文献 参考文献 暴力写法 二叉树 四叉树 树套树写法1 参考文献 四叉树 树套树 以及和zhoufangyuan巨佬的激烈♂讨论 参考文献 大家好我口糊大师又回来了. 给你一个\(n*n\)矩阵,然后让你支持两种操作,对子矩阵加值和对子矩阵查和. 暴力写法 对于每一行开一个线段树,然后跑,时间复杂度\(n^2logn\). 优点: 代码较短 较为灵活 缺点: 常数大 容易卡 二叉树 我们对于平面如此处理,一层维护横切,一层竖切. 当然,这个做法也是\(n^2logn\)的,卡法就是任意

数据结构:线段树

摘自<算法竞赛进阶指南>. 线段树是一种基于分治思想的二叉树结构,用于在区间上进行信息统计. 线段树的基本特征:1.线段树的每个节点都代表一个区间.2.线段树具有唯一的根节点,代表的区间是整个统计范围,如[1,N].3.线段树的每个叶节点都代表一个长度为1的元区间[x,x].4.对于每个内部节点[l,r],它的左子节点是[l,mid],右子节点是[mid+1,r],其中mid=(l+r)/2(向下取整). 线段树的节点编号方法:“父子二倍”节点编号法1.根节点编号为1.2.编号为x的节点的左子