线段树进阶

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 
View Code
技术图片
 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 
View Code

 

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 
View Code

 

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 
View Code

 

T4:

题干:

 

题解:

 

Code:

 

 

 

以上是关于线段树进阶的主要内容,如果未能解决你的问题,请参考以下文章

进阶线段树之乘法操作

[BZOJ4399]魔法少女LJJ----------线段树进阶

BZOJ4919[Lydsy1706月赛]大根堆-------------线段树进阶

线段树(等级2,待进阶)

线段树进阶之落花成泥

C++之路进阶——线段树(上帝造题的七分钟 2)