zoukankan      html  css  js  c++  java
  • 线段树

    线段树

      今天才发现博客里竟然没有叫做线段树的文章欸。(主席树 $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

  • 相关阅读:
    分析facebook的AsyncDisplayKit框架,async-display使用async-transaction
    自制反汇编逆向分析工具 迭代第四版本
    自制反汇编逆向分析工具 迭代第三版本
    自制反汇编逆向分析工具 迭代第二版本
    分析facebook的AsyncDisplayKit框架中的Transaction的工作原理
    Oracle索引知识学习笔记
    PDF文件添加二维码水印教程
    图片添加二维码水印教程
    CAS单点登录之支持数据库认证
    CAS单点登录之服务端部署
  • 原文地址:https://www.cnblogs.com/shzr/p/9763092.html
Copyright © 2011-2022 走看看