线段树

线段树

  今天才发现博客里竟然没有叫做线段树的文章欸。(主席树 $in$ 线段树)

  线段树是一种支持区间操作的数据结构...定义什么的就不说了吧。

  有的题目只是需要用到线段树优化,而关键的难度不在这里,这类的题目其实做过不少了,但是都比较水,相对比较难的就是那道“列队”了:https://www.cnblogs.com/shzr/p/9484250.html

  以下题目尽量按难易度排序,然而不保证递增qwq。

  线段树可以维护的信息合集:(不断更新中)

  1.区间加,区间求和。

  2.区间加,区间乘,区间求和;

    

  模板1(区间加,区间求和):https://www.luogu.org/problemnew/show/P3372

  其实也可以拿树状数组做,这里粘的是好久以前写的,码风比较奇怪.

  
 1 // luogu-judger-enable-o2
 2 # include <iostream>
 3 # include <cstdio>
 4 
 5 using namespace std;
 6 
 7 const  int maxn=400050; 
 8 
 9 int m,n;
10 long long a[100004]={0};
11 long long s[maxn]={0},lazy[maxn]={0};
12 
13 void update(int now)
14 {
15     s[now]=s[now<<1]+s[(now<<1)+1];
16 }
17 
18 void build (int now,int l,int r)
19 {
20     if (l==r) 
21     {
22         s[now]=a[l];
23         return;
24     }
25     int mid=(l+r)>>1;
26     build(now<<1,l,mid);
27     build((now<<1)+1,mid+1,r);
28     update(now);
29 }
30 
31 void pushdown(int now,int l,int r)
32 {
33     if(lazy[now])
34     {
35         int mid=(l+r)>>1;
36         s[now<<1]+=lazy[now]*(mid-l+1);
37         lazy[now<<1]+=lazy[now];
38         s[(now<<1)+1]+=lazy[now]*(r-mid);
39         lazy[(now<<1)+1]+=lazy[now];
40         lazy[now]=0;
41     }
42 }
43 
44 long long  ask(int now,int l,int r,int lrange,int rrange)
45 {
46     if(lrange<=l&&r<=rrange)
47       return s[now];
48     pushdown(now,l,r);
49     int mid=(l+r)>>1;
50     long long ans=0;
51     if (lrange<=mid) ans+=ask(now<<1,l,mid,lrange,rrange);
52     if (rrange>mid) ans+=ask((now<<1)+1,mid+1,r,lrange,rrange);
53     return ans;
54 }
55 
56 void change(int now,int l,int r,int lrange,int rrange,long long  v)
57 {
58     if(lrange<=l&&r<=rrange)
59     {
60         s[now]+=(r-l+1)*v;
61         lazy[now]+=v;
62         return;
63     }
64     pushdown(now,l,r);
65     int mid=(l+r)>>1;
66     if(lrange<=mid)
67       change(now<<1,l,mid,lrange,rrange,v);
68     if(rrange>mid)
69       change((now<<1)+1,mid+1,r,lrange,rrange,v);
70     update(now);
71 }
72 
73 int main()
74 {
75     int op;
76     int x,y;
77     long long k;
78     scanf("%d%d",&n,&m);
79     for (int i=1;i<=n;i++)
80       scanf("%lld",&a[i]);
81     build(1,1,n);
82     for (int i=1;i<=m;i++)
83     {
84         scanf("%d",&op);
85         if (op==1) 
86         {
87             scanf("%d%d%lld",&x,&y,&k);
88             change(1,1,n,x,y,k);
89         }
90         if (op==2)
91         {
92             scanf("%d%d",&x,&y);
93             cout<<ask(1,1,n,x,y)<<endl;
94         }
95     }
96     return 0;    
97 } 
Segment Tree-1

  模板2:(区间加,区间乘,区间求和):https://www.luogu.org/problemnew/show/P3373

  这道题其实不算特别难,但是引出了线段树类型中的一系列重要问题:标记的下放顺序,互相之间的影响,初始化;在这道题里有乘法和加法两个标记,每次有乘法标记时应当将加法标记乘上它。乘法标记的空状态应当为$1$.

  
  1 // luogu-judger-enable-o2
  2 # include <iostream>
  3 # include <cstdio>
  4 # include <algorithm>
  5 # define LL long long
  6 # define nl n<<1
  7 # define nr n<<1|1
  8 
  9 using namespace std;
 10 
 11 const int MAXN=1000008;
 12 
 13 int n,m;
 14 int x1,f1;
 15 LL x2,f2;
 16 char c;
 17 LL P;
 18 LL a[MAXN]={0};
 19 LL lazy_c[MAXN*4]={0},s[MAXN*4]={0},lazy_j[MAXN*4]={0};
 20 
 21 void update(int n)
 22 {
 23     s[n]=(s[nl]+s[nr])%P;
 24 }
 25 
 26 void pushdown_c(int n,int l,int r)
 27 {
 28     int mid=(l+r)>>1;
 29     lazy_j[nl]=(lazy_j[nl]*lazy_c[n])%P;
 30     lazy_j[nr]=(lazy_j[nr]*lazy_c[n])%P;
 31     lazy_c[nl]=(lazy_c[nl]*lazy_c[n])%P;
 32     lazy_c[nr]=(lazy_c[nr]*lazy_c[n])%P;
 33     s[nl]=(s[nl]*lazy_c[n])%P;
 34     s[nr]=(s[nr]*lazy_c[n])%P;
 35     lazy_c[n]=1;
 36 };
 37 
 38 void pushdown_j(int n,int l,int r)
 39 {
 40     int mid=(l+r)>>1;
 41     lazy_j[n]%=P;
 42     s[nl]=(s[nl]+lazy_j[n]*(mid-l+1))%P;
 43     lazy_j[nl]=(lazy_j[nl]+lazy_j[n])%P;
 44     s[nr]=(s[nr]+lazy_j[n]*(r-mid))%P;
 45     lazy_j[nr]=(lazy_j[nr]+lazy_j[n])%P;
 46     lazy_j[n]=0;
 47 }
 48 
 49 
 50 void build(int now,int l,int r)
 51 {
 52     if(l==r)
 53     {
 54         s[now]=a[l];
 55         lazy_c[now]=1;
 56         return;
 57     }
 58     int mid=(l+r)>>1;
 59     build(now<<1,l,mid);
 60     build((now<<1)+1,mid+1,r);
 61     lazy_c[now]=1;
 62     update(now);
 63 }
 64 
 65 void addc(int now,int l,int r,int ll,int rr,long long k)
 66 {
 67     k=k%P;
 68     if(ll<=l&&r<=rr)
 69     {
 70         lazy_c[now]=(lazy_c[now]*k)%P;
 71         lazy_j[now]=(lazy_j[now]*k)%P;
 72         s[now]=(s[now]*k)%P;
 73         return;
 74     }
 75     pushdown_c(now,l,r);
 76     if (lazy_j[now]) pushdown_j(now,l,r);
 77     int mid=(l+r)>>1;
 78     if(ll<=mid) addc(now<<1,l,mid,ll,rr,k);
 79     if(rr>mid)  addc((now<<1)+1,mid+1,r,ll,rr,k);
 80     update(now);
 81     return;
 82 }
 83 
 84 void addj(int now,int l,int r,int ll,int rr,long long k)
 85 {
 86     k=k%P;
 87     if(ll<=l&&r<=rr)
 88     {
 89         s[now]=(k*(r-l+1)+s[now])%P;
 90         lazy_j[now]=(k+lazy_j[now])%P;
 91         return ;
 92     }
 93     pushdown_c(now,l,r);
 94     if (lazy_j[now]) pushdown_j(now,l,r);
 95     int mid=(l+r)>>1;
 96     if(ll<=mid) addj(now<<1,l,mid,ll,rr,k);
 97     if(rr>mid)  addj((now<<1)+1,mid+1,r,ll,rr,k);
 98     update(now);
 99     return;
100 }
101 
102 LL ask(int now,int l,int r,int ll,int rr)
103 {
104     if(ll<=l&&r<=rr) return s[now]%P;
105     LL ans=0;
106     pushdown_c(now,l,r);
107     if (lazy_j[now]) pushdown_j(now,l,r);
108     int mid=(l+r)>>1;
109     if(ll<=mid) ans+=ask(now<<1,l,mid,ll,rr);
110     ans=ans%P;
111     if(rr>mid)  ans+=ask(now<<1|1,mid+1,r,ll,rr);
112     return ans%P;
113 }
114 
115 int readd()
116 {
117     x1=0; f1=1; 
118     c=getchar();
119     while (!isdigit(c))
120     {
121         if(c=='-') f1=-f1;
122         c=getchar();
123     }
124     while (isdigit(c))
125     {
126         x1=(x1<<3)+(x1<<1)+(c^48);
127         c=getchar();
128     }
129     return f1*x1;
130 }
131 
132 long long readdd()
133 {
134     x2=0; f2=1; 
135     c=getchar();
136     while (!isdigit(c))
137     {
138         if(c=='-') f2=-f2;
139         c=getchar();
140     }
141     while (isdigit(c))
142     {
143         x2=(x2<<3)+(x2<<1)+(c^48);
144         c=getchar();
145     }
146     return f2*x2;
147 }
148 
149 int main()
150 {
151     int x,x1,y;
152     LL k;
153     n=readd();
154     m=readd();
155     P=readdd();
156     for (register int i=1;i<=n;i++)
157       a[i]=readdd();
158     build(1,1,n);
159     for (register int i=1;i<=m;i++)
160     {
161         x=readd();
162         if(x==1)
163         {
164             x1=readd();
165             y=readd();
166             k=readdd();
167             addc(1,1,n,x1,y,k);
168         }
169         if(x==2)
170         {
171             x1=readd();
172             y=readd();
173             k=readdd();
174             addj(1,1,n,x1,y,k); 
175         }
176         if(x==3)
177         {
178             x1=readd();
179             y=readd();
180             printf("%lld
",ask(1,1,n,x1,y));    
181         }
182     }
183     return 0;    
184 }
Segment Tree-2

  降雨量:https://www.lydsy.com/JudgeOnline/problem.php?id=1067

  题意概述:给出$n$个年份的降雨量,再给出$m$个询问,形如:$x$年是$y$年以来降雨最多的,也就是说:$x$年的降雨量不超过$y$年,且对于任意$y<z<x$,$z$年的降雨量严格小于$x$年,用$true$,$false$,$maybe$来回答这些询问:$1<=n<=50000$,$1<=m<=10000$,$-10^9<=y_i<=10^9$,$1<=r_i<=10^9$

  这真是一道有名的题目啊!https://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=1108

  其实这个题挺简单的,就是细节比较多.首先年份可以非常大,但是年份的数量还可以接受,所以考虑离散化,将询问和给出的一起离散化,将知道的信息插入一棵线段树,每次询问即可.

  即可.

  这当然是不可能的,否则不就太简单了吗.

  离散化的时候不能普普通通的离散化,要插入一些空的年份,为什么?举个栗子:知道 $2018$ 年的降雨量是 $10$ , $2016$ 年的是 $12$ ,中间的那些年份都不清楚,如果只是简简单单的进行离散化,$2016$和$2018$就成了相邻的两个年份,本来应该是$maybe$的答案就成了$true$...所以离散化时如果两个年份不相邻就应当插入一个空的年份,如果已经相邻了就不能再插入了.

  关于判断:这里要充分利用题目给出的每一个条件,比如说:读入一个询问后发现$X$年根本就是未知的,于是想当然的以为这种情况没法判断,就输出$maybe$,然后就$WA$了.为什么?因为$Z<X<=Y$这个条件中即使不知道$X$,有时也是可以找到矛盾的,$Z>Y$.线段树在这个题目里存在感其实不高,关键是解的判断要细心,讨论情况时要做到不重不漏.

  
  1 // luogu-judger-enable-o2
  2 # include <cstdio>
  3 # include <iostream>
  4 # include <algorithm>
  5 # include <cstring>
  6 # define R register int
  7 # define nl (n<<1)
  8 # define nr (n<<1|1)
  9 
 10 using namespace std;
 11 
 12 const int maxn=50004;
 13 const int maxm=10004;
 14 const int maxt=maxn+maxn+maxm+maxm;
 15 int n,m,cnt,ans,maxx;
 16 int v[maxt],vy,vn;
 17 int y[maxn],r[maxn];
 18 int X[maxm],Y[maxm];
 19 int t[maxt<<2],h;
 20 bool c[maxt<<2],f;
 21 struct dig
 22 {
 23     int val,id,n_or_m;
 24 }a[maxn+maxm+maxm];
 25 
 26 bool cmp (dig a,dig b)
 27 {
 28     return a.val<b.val;
 29 }
 30 
 31 void ins (int n,int l,int r,int pos,int val)
 32 {
 33     if(l==r)
 34     {
 35         t[n]=val;
 36         c[n]=false;
 37         return;
 38     }
 39     int mid=(l+r)>>1;
 40     if(pos<=mid) ins(nl,l,mid,pos,val);
 41     else ins(nr,mid+1,r,pos,val);
 42     t[n]=max(t[nl],t[nr]);
 43     c[n]=c[nl]|c[nr];
 44 }
 45 
 46 int ask (int n,int l,int r,int ll,int rr)
 47 {    
 48     if(ll<=l&&r<=rr)
 49     {
 50         if(c[n]) f=true;
 51         return t[n];
 52     }
 53     int mid=(l+r)>>1,ans=0;
 54     if(ll<=mid) ans=max(ans,ask(nl,l,mid,ll,rr));
 55     if(rr>mid) ans=max(ans,ask(nr,mid+1,r,ll,rr));
 56     return ans;
 57 }
 58 
 59 int main()
 60 {
 61     scanf("%d",&n);
 62     for (R i=1;i<=n;++i)
 63     {
 64         scanf("%d%d",&y[i],&r[i]);
 65         a[++h].val=y[i];
 66         a[h].id=i;
 67         a[h].n_or_m=1;
 68     }
 69     scanf("%d",&m);
 70     for (R i=1;i<=m;++i)
 71     {
 72         scanf("%d%d",&Y[i],&X[i]);
 73         a[++h].val=X[i];
 74         a[h].id=i;
 75         a[h].n_or_m=2;
 76         a[++h].val=Y[i];
 77         a[h].id=i;
 78         a[h].n_or_m=3;
 79     }
 80     sort(a+1,a+1+h,cmp);
 81     a[0].val=a[1].val-1;
 82     for (R i=1;i<=h;++i)
 83     {
 84         if(a[i].val!=a[i-1].val)
 85         {
 86             if(a[i].val!=a[i-1].val+1) cnt++;
 87             cnt++;
 88         }
 89         if(a[i].n_or_m==1) y[ a[i].id ]=cnt;
 90         else if(a[i].n_or_m==2) X[ a[i].id ]=cnt;
 91         else Y[ a[i].id ]=cnt;
 92     }
 93     n=cnt;
 94     memset(c,true,sizeof(c));
 95     memset(v,-1,sizeof(v));
 96     for (R i=1;i<=n;++i)
 97         ins(1,1,n,y[i],r[i]),v[ y[i] ]=r[i];
 98     for (R i=1;i<=m;++i)
 99     {
100         f=false;
101         ans=0;
102         vy=v[ Y[i] ];
103         vn=v[ X[i] ];
104         if(vy<vn&&vy!=-1) { printf("false
"); continue; }
105         if(Y[i]+1>X[i]-1)
106         {
107             if(vy==-1||vn==-1) printf("maybe
");
108             else printf("true
");
109             continue;
110         }
111         maxx=ask(1,1,n,Y[i]+1,X[i]-1);
112         if(vy!=-1&&vn==-1&&maxx+1>vy) { printf("false
"); continue; }
113         if(maxx>=vn&&vn!=-1) { printf("false
"); continue; }
114         if(vn!=-1&&maxx<vn&&f==false&&vy!=-1) { printf("true
"); continue; }
115         printf("maybe
");
116     }
117     return 0;
118 }
降雨量

  

  区间交:https://www.51nod.com/Challenge/Problem.html#!#problemId=1672

  题意概述:给出一个长度为$n$,元素非负的整数序列,再给出$m$个闭区间,从中选出$k$个求交,得到的得分是交部分元素的和,求得分的最大值.$n,m,k<=10^5$

  因为元素非负,那么如果固定了开头,多选一些至少不会更劣,知道这一点就好做了.

  首先将区间按照左端点排序,从头开始加,用线段树维护每个点被多少个区间覆盖了,由于固定了左端点,这个值也是单调不增的,所以二分看从每一个点开始最长能延伸到那里,更新最优解即可.不过因为只有区间加和单点询问,用树状数组会更好一点.

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # include <algorithm>
 4 # define ll long long
 5 # define R register int
 6 # define lowbit(i) (i&(-i))
 7 
 8 using namespace std;
 9 
10 const int maxn=100005;
11 int h,t,n,k,m,c[maxn];
12 ll s[maxn],ans;
13 struct Interval
14 {
15     int l,r;
16 }q[maxn];
17 
18 bool cmp (Interval a,Interval b) 
19 { 
20     if(a.l==b.l) return a.r<b.r;
21     return a.l<b.l; 
22 }
23 
24 void add (int l,int r,int v)
25 {
26     r++;
27     for (R i=l;i<=n;i+=lowbit(i)) c[i]+=v;
28     for (R i=r;i<=n;i+=lowbit(i)) c[i]-=v;
29 }
30 
31 int ask (int x)
32 {
33     int ans=0;
34     for (R i=x;i;i-=lowbit(i)) ans+=c[i];
35     return ans;
36 }
37 
38 int f (int x)
39 {
40     int l=x,r=n,mid,ans=0;
41     while(l<=r)
42     {
43         mid=(l+r)>>1;
44         if(ask(mid)>=k)
45             ans=mid,l=mid+1;
46         else
47             r=mid-1;
48     }
49     return ans;
50 }
51 
52 int main()
53 {
54     scanf("%d%d%d",&n,&k,&m);
55     for (R i=1;i<=n;++i)
56         scanf("%lld",&s[i]),s[i]+=s[i-1];
57     for (R i=1;i<=m;++i)
58         scanf("%d%d",&q[i].l,&q[i].r); 
59     sort(q+1,q+1+m,cmp);
60     h=1,t=0;
61     for (R i=1;i<=n;++i)
62     {
63         while (q[t+1].l<=i&&t+1<=m) add(q[t+1].l,q[t+1].r,1),t=t+1; 
64         while (q[h].r<i&&h<=m) add(q[h].l,q[h].r,-1),h=h+1;
65         if(t-h+1<k) continue;
66         ans=max(ans,(s[ f(i) ]-s[i-1]));
67     }
68     printf("%lld",ans);
69     return 0;
70 }
区间交

  序列操作:https://www.lydsy.com/JudgeOnline/problem.php?id=1858

  题意概述:给出长度为n的01序列,要求支持以下操作,m次:$1<=n, m<=100000$

        区间覆盖0/1;

        区间取反;

       区间求和;

        区间求最长连续1;

  除了难写一点好像也没有什么别的难度.就是...确实难写.

  这些操作单独支持都不难,但是带上取反后的最长连续一怎么做呢?很简单,对于以前需要保留的关于1的信息,同时存一份0的用来备份,区间取反时将两份信息调换。

  注意:区间取反会将区间覆盖的标记取反,而区间覆盖可以直接覆盖区间取反。

  Update欣赏:                

  

  pushdown欣赏:

  

  
  1 # include <cstdio>
  2 # include <iostream>
  3 # include <cstring>
  4 # include <string>
  5 # define R register int
  6 # define nl (n<<1)
  7 # define nr (n<<1|1)
  8 
  9 using namespace std;
 10 
 11 const int maxn=100005;
 12 int n,m,opt,l,r;
 13 char st[4];
 14 struct node
 15 {
 16     int s,ans1,lans1,rans1,ans2,lans2,rans2,delta,siz;
 17 }t[maxn<<2],ans;
 18 
 19 char ss[1<<17],*A=ss,*B=ss;
 20 inline char gc()
 21 {
 22     if(A==B)
 23     {
 24         B=(A=ss)+fread(ss,1,1<<17,stdin);
 25         if(A==B)return EOF;
 26     }
 27     return *A++;
 28 }
 29 template<class T>inline void read(T&x)
 30 {
 31     char c;
 32     int y=1;
 33     while(c=gc(),c<48||57<c)
 34     if(c=='-')y=-1;x=c^48;
 35     while(c=gc(),47<c&&c<58)
 36         x=(x<<1)+(x<<3)+(c^48);
 37     x*=y;
 38 }
 39 
 40 void out (int x)
 41 {
 42     if(x>=10) out(x/10);
 43     putchar(x%10+'0');
 44 }
 45 
 46 node update (node a,node b)
 47 {
 48     node ans;
 49     ans.s=a.s+b.s;
 50     ans.ans1=max(a.ans1,max(b.ans1,a.rans1+b.lans1));
 51     ans.lans1=a.lans1;
 52     if(a.s==a.siz) ans.lans1=max(ans.lans1,a.s+b.lans1);
 53     ans.rans1=b.rans1;
 54     if(b.s==b.siz) ans.rans1=max(ans.rans1,b.s+a.rans1);
 55     ans.ans2=max(a.ans2,max(b.ans2,a.rans2+b.lans2));
 56     ans.lans2=a.lans2;
 57     if(a.s==0) ans.lans2=max(ans.lans2,a.siz+b.lans2);
 58     ans.rans2=b.rans2;
 59     if(b.s==0) ans.rans2=max(ans.rans2,b.siz+a.rans2);
 60     ans.siz=a.siz+b.siz;
 61     ans.delta=-1;
 62     return ans;
 63 }
 64 
 65 void build (int n,int l,int r)
 66 {
 67     if(l==r)
 68     {
 69         int x=0;
 70         read(x);
 71         if(x==1)
 72         {
 73             t[n].s=t[n].ans1=t[n].lans1=t[n].rans1=1;
 74             t[n].ans2=t[n].lans2=t[n].rans2=0;
 75         }
 76         else
 77         {
 78             t[n].s=t[n].ans1=t[n].lans1=t[n].rans1=0;
 79             t[n].ans2=t[n].lans2=t[n].rans2=1;
 80         }
 81         t[n].delta=-1;
 82         t[n].siz=1;
 83         return ;
 84     }
 85     int mid=(l+r)>>1;
 86     build(nl,l,mid);
 87     build(nr,mid+1,r);
 88     t[n]=update(t[nl],t[nr]);
 89 }
 90 
 91 void pushdown (int n,int l,int r)
 92 {
 93     int x=t[n].delta;
 94     t[n].delta=-1;
 95     if(x==2)
 96     {
 97         t[nl].s=t[nl].siz-t[nl].s;
 98         if(t[nl].delta==-1) t[nl].delta=2;
 99         else if(t[nl].delta==2) t[nl].delta=-1;
100         else t[nl].delta^=1;
101         swap(t[nl].ans1,t[nl].ans2);
102         swap(t[nl].lans1,t[nl].lans2);
103         swap(t[nl].rans1,t[nl].rans2);
104         t[nr].s=t[nr].siz-t[nr].s;
105         if(t[nr].delta==-1) t[nr].delta=2;
106         else if(t[nr].delta==2) t[nr].delta=-1;
107         else t[nr].delta^=1;
108         swap(t[nr].ans1,t[nr].ans2);
109         swap(t[nr].lans1,t[nr].lans2);
110         swap(t[nr].rans1,t[nr].rans2);
111     }
112     else if(x==1)
113     {
114         t[nl].delta=x;
115         t[nr].delta=x;
116         t[nl].s=t[nl].siz;
117         t[nl].ans1=t[nl].lans1=t[nl].rans1=t[nl].siz;
118         t[nl].ans2=t[nl].lans2=t[nl].rans2=0;
119         t[nr].s=t[nr].siz;
120         t[nr].ans1=t[nr].lans1=t[nr].rans1=t[nr].siz;
121         t[nr].ans2=t[nr].lans2=t[nr].rans2=0;
122     }
123     else
124     {
125         t[nl].delta=x;
126         t[nr].delta=x;
127         t[nl].s=0;
128         t[nl].ans1=t[nl].lans1=t[nl].rans1=0;
129         t[nl].ans2=t[nl].lans2=t[nl].rans2=t[nl].siz;
130         t[nr].s=0;
131         t[nr].ans1=t[nr].lans1=t[nr].rans1=0;
132         t[nr].ans2=t[nr].lans2=t[nr].rans2=t[nr].siz;
133     }
134 }
135 
136 void cov (int n,int l,int r,int ll,int rr,int col)
137 {
138     if(ll<=l&&r<=rr)
139     {
140         t[n].delta=col;
141         if(col==1)
142         {
143             t[n].s=t[n].siz;
144             t[n].ans1=t[n].lans1=t[n].rans1=t[n].siz;
145             t[n].ans2=t[n].lans2=t[n].rans2=0;
146         }
147         else
148         {
149             t[n].s=0;
150             t[n].ans1=t[n].lans1=t[n].rans1=0;
151             t[n].ans2=t[n].lans2=t[n].rans2=t[n].siz;
152         }
153         return ;
154     }
155     int mid=(l+r)>>1;
156     if(t[n].delta!=-1) pushdown(n,l,r);
157     if(ll<=mid) cov(nl,l,mid,ll,rr,col);
158     if(rr>mid) cov(nr,mid+1,r,ll,rr,col);
159     t[n]=update(t[nl],t[nr]);
160 }
161 
162 void chan (int n,int l,int r,int ll,int rr)
163 {
164     if(ll<=l&&r<=rr)
165     {
166         t[n].s=t[n].siz-t[n].s;
167         if(t[n].delta==-1) t[n].delta=2;
168         else if(t[n].delta==2) t[n].delta=-1;
169         else t[n].delta^=1;
170         swap(t[n].ans1,t[n].ans2);
171         swap(t[n].lans1,t[n].lans2);
172         swap(t[n].rans1,t[n].rans2);
173         return ;
174     }
175     int mid=(l+r)>>1;
176     if(t[n].delta!=-1) pushdown(n,l,r);
177     if(ll<=mid) chan(nl,l,mid,ll,rr);
178     if(rr>mid)  chan(nr,mid+1,r,ll,rr);
179     t[n]=update(t[nl],t[nr]);
180 }
181 
182 int ask (int n,int l,int r,int ll,int rr)
183 {
184     if(ll<=l&&r<=rr) return t[n].s;
185     int ans=0,mid=(l+r)>>1;
186     if(t[n].delta!=-1) pushdown(n,l,r);
187     if(ll<=mid) ans+=ask(nl,l,mid,ll,rr);
188     if(rr>mid) ans+=ask(nr,mid+1,r,ll,rr);
189     return ans;
190 }
191 
192 node cont (int n,int l,int r,int ll,int rr)
193 {
194     if(ll<=l&&r<=rr) return t[n];
195     int mid=(l+r)>>1;
196     if(t[n].delta!=-1) pushdown(n,l,r);
197     if(rr<=mid) return cont(nl,l,mid,ll,rr);
198     else if(ll>mid) return cont(nr,mid+1,r,ll,rr);
199     return update(cont(nl,l,mid,ll,rr),cont(nr,mid+1,r,ll,rr));
200 }
201 
202 int main()
203 {
204     read(n),read(m);
205     build(1,1,n);   
206     for (R i=1;i<=m;++i)
207     {
208         read(opt);
209         read(l);
210         read(r);
211         l++,r++;
212         if(opt==0)
213             cov(1,1,n,l,r,0);
214         else if(opt==1)
215             cov(1,1,n,l,r,1);
216         else if(opt==2)
217             chan(1,1,n,l,r);
218         else if(opt==3)
219             out(ask(1,1,n,l,r)),putchar('
');
220         else if(opt==4)
221         {
222             ans=cont(1,1,n,l,r);
223             out(ans.ans1);
224             putchar('
');
225         }
226     }  
227     return 0;
228 }
序列操作

 

  色版游戏:https://www.luogu.org/problemnew/show/P1558

  题意概述:维护一个长度为$n$的序列,要求支持区间覆盖,区间询问有几种不同颜色.$n<=10^6$,颜色总数不超过30.

  线段树的每个节点保留一个二进制数,表示这些颜色在这一段出现过.说这一道题的目的是不要思维僵化,线段树能存的信息很多.甚至还可以每个节点再存一个数据结构.

  
  1 // luogu-judger-enable-o2
  2 # include <cstdio>
  3 # include <iostream>
  4 # define R register int
  5 # define nl n<<1
  6 # define nr n<<1|1
  7 
  8 using namespace std;
  9 
 10 const int maxn=100009;
 11 int xx,cc,n,t,o,l,r,co,s;
 12 long long c[maxn<<2],delta[maxn<<2],ans;
 13 char ch;
 14 
 15 void build(int n,int l,int r)
 16 {
 17     delta[n]=-1;
 18     if(l==r)
 19     {
 20         c[n]=1;
 21         return ;
 22     }
 23     int mid=(l+r)>>1;
 24     build(nl,l,mid);
 25     build(nr,mid+1,r);
 26     c[n]=c[nl]|c[nr];
 27 }
 28 
 29 void pushdown(int n,int l,int r)
 30 {
 31     int x=delta[n];
 32     delta[n]=-1;
 33     delta[nl]=x;
 34     delta[nr]=x;
 35     c[nl]=(1<<(x-1));
 36     c[nr]=(1<<(x-1));
 37     c[n]=c[nl]|c[nr];
 38 }
 39 
 40 void chan (int n,int l,int r,int ll,int rr,int co)
 41 {
 42     if(ll<=l&&r<=rr)
 43     {
 44         delta[n]=co;
 45         c[n]=(1<<(co-1));
 46         return ;
 47     }
 48     if(delta[n]!=-1) pushdown(n,l,r);
 49     int mid=(l+r)>>1;
 50     if(ll<=mid)chan(nl,l,mid,ll,rr,co);
 51     if(rr>mid)chan(nr,mid+1,r,ll,rr,co);
 52     c[n]=c[nl]|c[nr];
 53 }
 54 
 55 long long ask (int n,int l,int r,int ll,int rr)
 56 {
 57     if(ll<=l&&r<=rr)
 58         return c[n];
 59     if(delta[n]!=-1) pushdown(n,l,r);
 60     int mid=(l+r)>>1;
 61     long long ans=0;
 62     if(ll<=mid) ans=ans|ask(nl,l,mid,ll,rr);
 63     if(rr>mid)  ans=ans|ask(nr,mid+1,r,ll,rr);
 64     return ans;
 65 }
 66 
 67 int read()
 68 {
 69     xx=0;
 70     cc=getchar();
 71     while (!isdigit(cc)) cc=getchar();
 72     while (isdigit(cc))  xx=(xx<<3)+(xx<<1)+(cc^48),cc=getchar();
 73     return xx;
 74 }
 75 
 76 int main()
 77 {
 78     n=read(),t=read(),o=read();
 79     build(1,1,n);
 80     for (R i=1;i<=o;i++)
 81     {
 82         ch=getchar();    
 83         while (ch!='P'&&ch!='C') ch=getchar();
 84         if(ch=='C')
 85         {
 86             l=read(),r=read(),co=read();
 87             if(l>r) swap(l,r);
 88             chan(1,1,n,l,r,co);    
 89         }
 90         else
 91         {
 92             l=read(),r=read();
 93             if(l>r) swap(l,r);
 94             ans=ask(1,1,n,l,r);
 95             s=0;
 96             for (int i=0;i<=t;i++)
 97                 if (ans&(1<<i)) s++;
 98             printf("%d
",s);
 99         }
100     }
101     return 0;
102 }
色板游戏

 

  方差:https://www.luogu.org/problemnew/show/P1471

  题意概述:区间加,区间求平均数,区间求方差.

  见到这么一道题,首先考虑将方差的公式拆开,变的可以用线段树维护.

  $s^2=frac{sum_{i=1}^n(x_i-overline{x})^2}{n}$

  $n imes s^2=sum_{i=1}^n(x_i-overline{x})^2$

  $=sum_{i=1}^n(x_i^2-2x_ioverline{x}+overline{x}^2)$

  $=sum_{i=1}^nx_i^2+2 overline{x}sum_{i=1}^nx_i+n imes overline{x}^2$

  其实到这里好像还是可以再化一下的,但是反正也要问平均数,我选择先查询一次平均数,再到线段树上算刚刚那个式子.每个节点维护区间和以及区间平方和.

  这里还有一个区间加,对于区间和可以直接加,平方和就把完全平方公式拆开来用好了.注意:更新平方和时还要用到区间和,这里的区间和应该是修改以前的,所以先改平方和再改普通和.

  
  1 // luogu-judger-enable-o2
  2 # include <cstdio>
  3 # include <iostream>
  4 # include <cstring>
  5 # define nl n<<1
  6 # define nr n<<1|1
  7 # define R register int
  8 
  9 using namespace std;
 10 
 11 const int maxn=100005;
 12 int n,m,ll,rr;
 13 double a[maxn]={0};
 14 double delta[maxn*4]={0},s[maxn*4]={0},ss[maxn*4]={0};
 15 
 16 inline void update(int n)
 17 {
 18     s[n]=s[nl]+s[nr];
 19     ss[n]=ss[nl]+ss[nr];
 20 }
 21 
 22 void build(int n,int l,int r)
 23 {
 24     if(l==r)
 25     {
 26         s[n]=a[l];
 27         ss[n]=a[l]*a[l];
 28         return;
 29     }
 30 
 31     int mid=(l+r)>>1;
 32     build(nl,l,mid);
 33     build(nr,mid+1,r);
 34     update(n);
 35 }
 36 
 37 void pushdown(int n,int l,int r)
 38 {
 39     double x=delta[n];
 40     int mid=(l+r)>>1;
 41     delta[nl]+=x;
 42     delta[nr]+=x;
 43     ss[nl]+=(mid-l+1)*x*x+2*s[nl]*x;
 44     ss[nr]+=(r-mid)*x*x+2*s[nr]*x;
 45     s[nl]+=x*(mid-l+1);
 46     s[nr]+=x*(r-mid);
 47     delta[n]=0;
 48 }
 49 
 50 double ask(int n,int l,int r,int ll,int rr)
 51 {
 52     if(ll<=l&&r<=rr)
 53       return s[n];
 54       
 55     if(delta[n]) pushdown(n,l,r);
 56     int mid=(l+r)>>1;
 57     double ans=0;
 58     if(ll<=mid) ans+=ask(nl,l,mid,ll,rr);
 59     if(rr>mid)   ans+=ask(nr,mid+1,r,ll,rr);
 60     return ans;    
 61 }
 62 
 63 double askk(int n,int l,int r,int ll,int rr,double avg)
 64 {
 65     if(ll<=l&&r<=rr)
 66       return (ss[n]+(r-l+1)*avg*avg-2*avg*s[n]);
 67     if(delta[n]) pushdown(n,l,r);
 68     int mid=(l+r)>>1;
 69     double ans=0;
 70     if(ll<=mid) ans+=askk(nl,l,mid,ll,rr,avg);
 71     if(rr>mid)   ans+=askk(nr,mid+1,r,ll,rr,avg);
 72     return ans;
 73 }
 74 
 75 void add(int n,int l,int r,int ll,int rr,double x)
 76 {
 77     if(ll<=l&&r<=rr)
 78     {
 79       delta[n]+=x;
 80       ss[n]+=x*x*(r-l+1)+2*x*s[n];
 81       s[n]+=(r-l+1)*x;
 82       return ;
 83     }
 84     pushdown(n,l,r);
 85     int mid=(l+r)>>1;
 86     if(ll<=mid) add(nl,l,mid,ll,rr,x);
 87     if(rr>mid)  add(nr,mid+1,r,ll,rr,x);
 88     update(n);
 89     return ;
 90 }
 91 
 92 int main()
 93 {
 94     scanf("%d%d",&n,&m);
 95     int op;//指示剂 
 96     double k;
 97     for (R i=1;i<=n;i++)
 98       scanf("%lf",&a[i]);
 99     build(1,1,n);
100     for (R i=1;i<=m;i++)
101     {
102         scanf("%d",&op);
103         if(op==1)
104         {
105             scanf("%d%d",&ll,&rr);
106             cin>>k;
107             add(1,1,n,ll,rr,k);
108         }
109         else if(op==2)
110         {
111             scanf("%d%d",&ll,&rr);
112             printf("%0.4lf
",ask(1,1,n,ll,rr)/(double)(rr-ll+1));
113         }
114         else
115         {
116             scanf("%d%d",&ll,&rr);
117             double avg=ask(1,1,n,ll,rr);
118             avg=avg/(double)(rr-ll+1);
119             printf("%0.4lf
",askk(1,1,n,ll,rr,avg)/(double)(rr-ll+1));
120         }
121     }
122     return 0;
123 }
方差

 

  小白逛公园:https://www.luogu.org/problemnew/show/P4513

  题意概述:单点修改,区间最大连续子段和.

  每个区间维护以下信息:最大子段和,从左端点开始的最大子段和,从右开始的最大子段和,区间总和.

  上传时:

  最大子段和为:(左儿子最大子段和,右儿子最大子段和,左儿子从右端点的最大子段和+右儿子从左端点的最大子段和)取最大值.

  
  1 # include <cstdio>
  2 # include <iostream>
  3 # define R register int
  4 # define nl (n<<1)
  5 # define nr (n<<1|1)
  6 
  7 int min (int a,int b) { if(a>b) return b; return a; }
  8 int max (int a,int b) { if(a<b) return b; return a; }
  9 const int maxn=500009;
 10 int k,x,y,n,m,a[maxn];
 11 struct nod
 12 {
 13     int lm,rm,ans,sum;
 14 }t[maxn<<2],ans;
 15 
 16 nod update (nod a,nod b)
 17 {
 18     nod ans;
 19     ans.sum=a.sum+b.sum;
 20     ans.lm=max(a.lm,a.sum+b.lm);
 21     ans.rm=max(b.rm,b.sum+a.rm);
 22     ans.ans=max(a.ans,max(b.ans,a.rm+b.lm));
 23     return ans;
 24 }
 25 
 26 void build (int n,int l,int r)
 27 {
 28     if(l==r)
 29     {
 30         t[n].ans=a[l];
 31         t[n].lm=a[l];
 32         t[n].rm=a[l];
 33         t[n].sum=a[l];
 34         return ;
 35     }
 36     int mid=(l+r)>>1;
 37     build(nl,l,mid);
 38     build(nr,mid+1,r);
 39     t[n]=update(t[nl],t[nr]);
 40 }
 41 
 42 int read()
 43 {
 44     int x=0,f=1;
 45     char c=getchar();
 46     while (!isdigit(c))
 47     {
 48         if(c=='-') f=-f;
 49         c=getchar();
 50     }
 51     while (isdigit(c))
 52     {
 53         x=(x<<3)+(x<<1)+(c^48);
 54         c=getchar();
 55     }
 56     return x*f;    
 57 }
 58 
 59 void change (int n,int l,int r,int pos,int v)
 60 {
 61     if(l==pos&&pos==r)
 62     {
 63         t[n].ans=v;
 64         t[n].lm=v;
 65         t[n].rm=v;
 66         t[n].sum=v;
 67         return ;
 68     }
 69     int mid=(l+r)>>1;
 70     if(pos<=mid) change(nl,l,mid,pos,v);
 71     if(pos>mid) change(nr,mid+1,r,pos,v);
 72     t[n]=update(t[nl],t[nr]);
 73 }
 74 
 75 nod ask(int n,int l,int r,int ll,int rr)
 76 {
 77     if(ll<=l&&r<=rr)
 78         return t[n];
 79     int mid=(l+r)>>1;
 80     nod a,b;
 81     a.ans=a.lm=a.rm=a.sum=0;
 82     b.ans=b.lm=b.rm=b.sum=0;
 83     if(ll<=mid) a=ask(nl,l,mid,ll,rr);
 84     if(rr>mid) b=ask(nr,mid+1,r,ll,rr);
 85     if(ll<=mid&&rr>mid) return update(a,b);
 86     if(ll<=mid&&rr<=mid) return a;
 87     return b;
 88 }
 89 
 90 int main()
 91 {
 92     n=read(),m=read();
 93     for (R i=1;i<=n;++i)
 94         a[i]=read();
 95     build(1,1,n);
 96     for (R i=1;i<=m;++i)
 97     {
 98         k=read(),x=read(),y=read();
 99         if(k==1)
100         {
101             if(x>y) std::swap(x,y);
102             ans=ask(1,1,n,x,y);    
103             printf("%d
",ans.ans);
104         }
105         else
106             change(1,1,n,x,y);
107     }
108     return 0;
109 }
小白逛公园

 

  排序:https://lydsy.com/JudgeOnline/problem.php?id=4552

  题意概述:给出一个$1-n$的全排列,进行$m$次局部排序,最后询问一次第$q$个位置上的数.$n,m<=50000$

  这道题好神奇啊,完全看不出和线段树有什么关系,线段树还可以排序吗?其实是可以的,把思路放宽一点,如果只有$0,1$序列,显然是可以用线段树排序的,也就是说,如果想用线段树进行排序,必须要借助桶排的思想才行.对于这道题,可以发现只询问一次,而且可以先把这个询问位置读进来再进行排序操作.考虑二分,将大于等于二分值的数设为1,否则为0,用线段树进行一番排序,如果这时$q$为1,说明这个位置上的数至少是二分值.写起来不大难,但是这个思想还是挺妙的.

  
  1 # include <cstdio>
  2 # include <iostream>
  3 # define nl (n<<1)
  4 # define nr (n<<1|1)
  5  
  6 using namespace std;
  7  
  8 const int maxn=200005;
  9 int n,m,a[maxn],t[maxn<<2],delta[maxn<<2],l[maxn],r[maxn],op[maxn],L,R,mid,ans,pos;
 10  
 11 void build (int n,int l,int r,int base)
 12 {
 13     if(l==r)
 14     {
 15         if(a[l]>=base) t[n]=1;
 16         else t[n]=0;
 17         delta[n]=-1;
 18         return;
 19     }
 20     int mid=(l+r)>>1;
 21     build(nl,l,mid,base);
 22     build(nr,mid+1,r,base);
 23     t[n]=t[nl]+t[nr];
 24     delta[n]=-1;
 25 }
 26  
 27 void pushdown (int n,int l,int r)
 28 {
 29     int mid=(l+r)>>1;
 30     delta[nl]=delta[n];
 31     delta[nr]=delta[n];
 32     if(delta[n]==1) t[nl]=mid-l+1;
 33     if(delta[n]==1) t[nr]=r-mid;
 34     if(delta[n]==0) t[nl]=0;
 35     if(delta[n]==0) t[nr]=0;
 36     delta[n]=-1;
 37 }
 38  
 39  
 40 int ask (int n,int l,int r,int ll,int rr)
 41 {
 42     if(ll<=l&&r<=rr) return t[n];
 43     int mid=(l+r)>>1,ans=0;
 44     if(delta[n]!=-1) pushdown(n,l,r);
 45     if(ll<=mid) ans+=ask(nl,l,mid,ll,rr);
 46     if(rr>mid) ans+=ask(nr,mid+1,r,ll,rr);
 47     return ans;
 48 }
 49  
 50 void change (int n,int l,int r,int ll,int rr,int col)
 51 {
 52     if(ll<=l&&r<=rr)
 53     {
 54         delta[n]=col;
 55         if(col==1) t[n]=r-l+1;
 56         else t[n]=0;
 57         return ;
 58     }
 59     int mid=(l+r)>>1;
 60     if(delta[n]!=-1) pushdown(n,l,r);
 61     if(ll<=mid) change(nl,l,mid,ll,rr,col);
 62     if(rr>mid) change(nr,mid+1,r,ll,rr,col);
 63     t[n]=t[nl]+t[nr];
 64 }
 65  
 66 bool check (int x)
 67 {
 68     build(1,1,n,x);
 69     int cnt;
 70     for (register int i=1;i<=m;++i)
 71     {
 72         cnt=ask(1,1,n,l[i],r[i]);
 73         if(cnt==0) continue;
 74         change(1,1,n,l[i],r[i],0);
 75         if(op[i]) change(1,1,n,l[i],l[i]+cnt-1,1);
 76         else change(1,1,n,r[i]-cnt+1,r[i],1);
 77     }
 78     return ask(1,1,n,pos,pos)==1;
 79 }
 80  
 81 inline int read()
 82 {
 83     register int x=0;
 84     register char c=getchar();
 85     while (!isdigit(c)) c=getchar();
 86     while (isdigit(c)) { x=(x<<3)+(x<<1)+(c^48); c=getchar(); }
 87     return x;
 88 }
 89  
 90 int main()
 91 {
 92     scanf("%d%d",&n,&m);
 93     for (register int i=1;i<=n;++i)
 94         a[i]=read();
 95     for (register int i=1;i<=m;++i)
 96     {
 97         op[i]=read(),l[i]=read(),r[i]=read();
 98         if(l[i]>r[i]) swap(l[i],r[i]);
 99     }
100     L=1,R=n,ans=1;
101     scanf("%d",&pos);
102     while (L<=R)
103     {
104         mid=(L+R)>>1;
105         if(check(mid)) ans=max(ans,mid),L=mid+1;
106         else R=mid-1;
107     }
108     printf("%d",ans);
109     return 0;
110 }
排序

  无聊的数列:https://www.luogu.org/problemnew/show/P1438

  题意概述:区间加等差数列,单点查询.$n,m<=10^5$

  等差数列的性质:差分后所有数均相等,所以可以转化为区间加.单点查询时就查询前缀和即可.

  
  1 # include <cstdio>
  2 # include <iostream>
  3 # define nl n<<1
  4 # define nr n<<1|1
  5 # define R register int
  6 
  7 using namespace std;
  8 
  9 const int N=100079;
 10 
 11 int m,n,l,r,k,d,x,rx,rf;
 12 char rc;
 13 long long ans=0;
 14 int c[N<<2]={0},a[N]={0},delta[N<<2]={0};
 15 
 16 inline int readd()
 17 {
 18     rx=0;
 19     rf=1;
 20     rc=getchar();
 21     while (!isdigit(rc))
 22     {
 23         if(rc=='-') rf=-rf;
 24         rc=getchar();
 25     }
 26     while (isdigit(rc))
 27     {
 28         rx=(rx<<3)+(rx<<1)+(rc^48);
 29         rc=getchar();
 30     }
 31     return rx*rf;
 32 }
 33 
 34 void build(int n,int l,int r)
 35 {
 36     delta[n]=0;
 37     if(l==r)
 38     {
 39         c[n]=a[r];
 40         return;
 41     }
 42     int mid=(l+r)>>1;
 43     build(nl,l,mid);
 44     build(nr,mid+1,r);
 45     c[n]=c[nl]+c[nr];
 46 }
 47 
 48 void pushdown(int n,int l,int r)
 49 {
 50     if(delta[n])
 51     {
 52         int mid=(l+r)>>1;
 53         int x=delta[n];
 54         delta[n<<1]+=x;
 55         delta[n<<1|1]+=x;
 56         delta[n]=0;
 57         c[nl]+=(mid-l+1)*x;
 58         c[nr]+=(r-mid)*x;
 59     }
 60 }
 61 
 62 
 63 long long ask(int n,int l,int r,int ll,int rr)
 64 {
 65     if(ll>r||rr<l) return 0;
 66     if(ll<=l&&r<=rr)
 67       return c[n];
 68     pushdown(n,l,r);
 69     int mid=(l+r)>>1;
 70     return ask(nl,l,mid,ll,rr)+ask(nr,mid+1,r,ll,rr);    
 71 }
 72 
 73 void add(int n,int l,int r,int ll,int rr,int x)
 74 {
 75     if(ll>r||rr<l) return ;
 76     if(ll<=l&&r<=rr)
 77     {
 78         delta[n]+=x;
 79         c[n]+=(r-l+1)*x;
 80         return ;
 81      }
 82     pushdown(n,l,r);
 83     int mid=(l+r)>>1;
 84     add(nl,l,mid,ll,rr,x);
 85     add(nr,mid+1,r,ll,rr,x);
 86     c[n]=c[nl]+c[nr];    
 87 }
 88 
 89 int main()
 90 {
 91     n=readd();
 92     m=readd();
 93     l=0;
 94     for (R i=1;i<=n;i++)
 95     {
 96         x=readd();
 97         a[i]=x-l;
 98         l=x;        
 99     }
100     a[n+1]=l-x;
101     build(1,1,n);            
102     for (R i=1;i<=m;i++)
103     {
104         x=readd();
105         x--;
106         if(x) //查询 
107         {
108             scanf("%d",&l);
109             ans=ask(1,1,n,1,l);
110             printf("%lld
",ans);
111         }
112         else  //
113         {
114             scanf("%d%d%d%d",&l,&r,&k,&d);
115             add(1,1,n,l+1,r,d);
116             add(1,1,n,l,l,k);
117             add(1,1,n,r+1,r+1,-k-(r-l)*d);
118         }
119     }
120     return 0;
121 }
无聊的数列

  

  花神游历各国:https://www.luogu.org/problemnew/show/P4145

  题意概述:区间开平方并下取整,区间求和.$n,m<=10^5,a_i<=10^{12}$

  看起来好像很玄妙,其实很简单,因为每个数不会变大,只会不停地被开方,而即使每个数都达到值域上界,开上六次平方也就成1了,所以维护每个区间的最大值,如果不是1或0就暴力开方,否则就不用开了.

  
 1 # include <cstdio>
 2 # include <iostream>
 3 # include <cmath>
 4 # define nl (n<<1)
 5 # define nr (n<<1|1)
 6 # define R register int
 7 
 8 using namespace std;
 9 
10 const int maxn=100005;
11 int n,m,k,l,r;
12 long long t[maxn<<2],s[maxn<<2];
13 
14 void build (int n,int l,int r)
15 {
16     if(l==r)
17     {
18         scanf("%lld",&s[n]);
19         t[n]=s[n];
20         return;    
21     }
22     int mid=(l+r)>>1;
23     build(nl,l,mid);
24     build(nr,mid+1,r);
25     s[n]=s[nl]+s[nr];
26     t[n]=max(t[nl],t[nr]);
27 }
28 
29 long long ask (int n,int l,int r,int ll,int rr)
30 {
31     if(ll<=l&&r<=rr)
32         return s[n];
33     int mid=(l+r)>>1;
34     long long ans=0;
35     if(ll<=mid) ans+=ask(nl,l,mid,ll,rr);
36     if(rr>mid)  ans+=ask(nr,mid+1,r,ll,rr);
37     return ans;
38 }
39 
40 void change (int n,int l,int r,int ll,int rr)
41 {
42     if(ll<=l&&r<=rr&&t[n]<=1)
43         return;
44     if(l==r)
45     {
46         s[n]=(long long)sqrt(s[n]);
47         t[n]=s[n];
48         return;
49     }
50     int mid=(l+r)>>1;
51     if(ll<=mid) change(nl,l,mid,ll,rr);
52     if(rr>mid) change(nr,mid+1,r,ll,rr);
53     s[n]=s[nl]+s[nr];
54     t[n]=max(t[nl],t[nr]);
55 }
56 
57 int read()
58 {
59     int x=0;
60     char c=getchar();
61     while (!isdigit(c)) c=getchar();
62     while (isdigit(c))  x=(x<<3)+(x<<1)+(c^48),c=getchar();
63     return x;
64 }
65 
66 int main()
67 {
68     scanf("%d",&n);
69     build(1,1,n);
70     scanf("%d",&m);
71     for (R i=1;i<=m;++i)
72     {
73         k=read(),l=read(),r=read();
74         if(l>r) swap(l,r); 
75         if(k==1) printf("%lld
",ask(1,1,n,l,r));
76         else change(1,1,n,l,r);
77     }
78     return 0;
79 }
花神游历各国

  

    HH的项链:https://www.luogu.org/problemnew/show/P1972

  题意概述:求区间内不同元素数量,多组询问,资瓷离线。$n,m<=5 imes 10^5$

  方法一:莫队。莫队做法非常显然,但是可能会T;

  方法二:树状数组。考虑右端点不断向右移动,对于每种颜色就可以只考虑最右边的那个,而左边的再也不会用到了。利用这个性质,每次向右移动时,之前那个扔掉,加上新的,这个过程用树状数组来实现(一个位置如果是一,表示这里出现了一个(对于目前的右端点)后面没有再出现过的颜色)所以区间的和就是答案。询问按照右端点排序,离线做。这个做法可以很方便的推广,即使用可持久化数据结构如主席树来代替树状数组,保留以每个点为右端点时的信息。

  方法三:二维数点。将每个数字视为一个点(x,pre[x](上次出现位置)),对于每个询问求满足以下条件的点的数目(l<=x<=r,0<=pre[x]<l),可以用树状数组离线做,也可以主席树在线。这个做法和上一个做法还是有一定区别的,上一个是保留最靠右边的那个,而这个做法等价于保留最左边的一个。

  贪婪大陆:https://www.luogu.org/problemnew/show/P2184

  康娜的线段树:https://www.luogu.org/problemnew/show/P3924

  梦美与线段树:https://www.luogu.org/problemnew/show/P4927

 

  CF893F:https://www.luogu.org/problemnew/show/CF893F

  题意概述:给定一棵树,多组询问,求以 $x$ 节点为根的子树中与它距离不超过 $k$ 的点的点权最小值,强制在线。$n,m<=10^6$

  一道挺有趣的题目,先想一下离线有什么好做法?好像没有...可以dsu on tree做到两个log。

  直接在线吧。一个比较简单的想法是变成一种广义上的二维数点,给一个点二维权值 $(a,b)$,分别表示深度和dfs序。那么,每次要统计的就是:

  $min{v(a,b)|dep_x<=a<=dep_x+k,id_x<=b<=id_x+siz_x-1}$

  这好像很难做啊。简化一下,如果是求和,怎么做?可以将 $a$ 作为一维,$b$ 作为一维,用主席树来做。但这种方法要求所求的值具有可减性,所以好像不能做。

  但其实是可以的,想一想,如果只是查前缀某某,是不是就不需要具有可减性了?这道题恰好就有这样的性质。

  可以发现,虽然要数的点似乎有四条限制,但是一个点的子树内的点不可能比他的深度还小,所以第一条性质加了等于白加...即:

  $min{v(a,b)|a<=dep_x+k,id_x<=b<=id_x+siz_x-1}$

  这样就可以做了,将 $a$ 作为主席树的第一维,只需要查询一段前缀区间...感觉说的有点不明不白,总之还是看式子理解一下吧。这个做法一个log,还挺巧妙的。代码没有。

 ---shzr

原文地址:https://www.cnblogs.com/shzr/p/9763092.html