线段树进阶
Posted oi-zzyy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树进阶相关的知识,希望对你有一定的参考价值。
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:
以上是关于线段树进阶的主要内容,如果未能解决你的问题,请参考以下文章
[BZOJ4399]魔法少女LJJ----------线段树进阶