• [loj2461]完美的队列


    参考论文,这里一共写了论文中的3种做法,第一种做法为强制在线时的做法,第二种为时间复杂度略高的做法(前两种都无法通过),第三种为本题正解,并给出了一种理论复杂度更优的做法

    1.做法1

    情况1

    $forall 1le ile n,a_{i}=1$

    此时相当于维护一个序列,要求支持区间覆盖&求值的种类数

    考虑去将相邻且权值(操作编号)相同的缩为一个段,用set来维护这些段,具体来说,对于每一段维护一个起点以及权值,将其作为set中的一个二元组,按起点从小到大排序

    (特别的,为了避免有空隙而无法被表示,初始假设所有元素都为0)

    对于一个操作,也就是将起点在$[l,r]$中的元素删除,并加入$(l,now)$以及$(r+1,last)$即可(其中$now$指当前操作的权值,$last$指插入前起点不超过$r$且最大的二元组的权值

    (若已经存在以$r+1$为起点的二元组,则不插入$(r+1,last)$)

    暴力删除即可,显然由于每一次至多产生两个元素,因此总时间复杂度为$o(nlog n)$

    另外,关于答案的维护,直接统计所有二元组中每一个非0权值各有几个,在修改时维护即可

    情况2

    $forall 1le ile n,a_{i}=k$

    延续之前的做法,考虑去维护$k$个set

    具体来说,将所有队列中的元素按队尾对齐,然后对于纵向的每一列维护一个set,每一次本要删除的元素加入下一个set中即可(对于最后一个set直接删除)

    在加入下一个set时,我们要将所有元素一起插入,即先将其作为一个整体的$[l,r]$插入下一个set中,再将其中具体的每一段插入,避免新建过多节点

    时间复杂度上,即每次操作至多产生$2k$个元素,且每一个元素至多被后移$k$次,因此总复杂度为$o(k^{2}nlog n)$

    这个做法可以优化,如果通过splay来手动实现set,将这个区间在splay中分离出来,并直接接入到下一个中即可,这样的删除就不是均摊,而是每一次操作严格操作$k$次,总复杂度为$o(knlog n)$

    情况3

    $forall 1le ile n,a_{i}le k$

    此时先将其作为$forall 1le ile n,a_{i}=k$来做,并预处理ST表来支持区间最小值,当某一个区间内$a_{i}$的最大值都小于当前set的编号则将其权值改为0(统计的是非0权值)

    这样我们需要对所有操作的区间挨个检验,那么之前使用splay来优化并没有意义,仍为$o(k^{2}nlog n)$

    考虑对每一个二元组再加一个权值,表示其区间内$a_{i}$的最大值,并用splay维护子树内的最小值,当发现存在最小值<set的编号,则找到最小值来源并删除即可

    此时,由于每一个区间在被删除时才会有贡献,总复杂度变为$o(knlog n)$

    情况4

    $sum_{i=1}^{n}a_{i}le 10^{6}$

    这一情况下,将$a_{i}$分为两部分:

    1.对于$a_{i}le K$,将这些位置提出来变为一个另一个问题去做

    2.对于$a_{i}>K$,这些位置数不超过$frac{10^{6}}{K}$,每一次操作暴力检验是否影响到并插入即可

    复杂度为$o(Knlog n+frac{10^{6}n}{K})$,取$K=sqrt{frac{10^{6}}{log n}}$,复杂度即为$o(nsqrt{10^{6}log n})$,常数略卡

    (实际情况下,由于空间问题,只能取$K=30$)

    代码

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define N 100005
      4 #define s(p) f[k].ch[p]
      5 struct node{
      6     int fa,ch[2],st,ed,val,lim,mn;
      7 }f[N*61];
      8 vector<int>v;
      9 queue<int>q[N];
     10 int V,K,n,m,l,r,x,ans,a[N],Log[N],ST[N][21],rt[N],tot[N];
     11 void add(int k){
     12     if (++tot[k]==1)ans++;
     13 }
     14 void dec(int k){
     15     if (--tot[k]==0)ans--;
     16 }
     17 int get_max(int x,int y){
     18     int p=Log[y-x+1];
     19     return min(max(ST[x][p],ST[y-(1<<p)+1][p]),K);
     20 }
     21 bool pd(int k){
     22     return f[f[k].fa].ch[1]==k;
     23 }
     24 int New(int st,int ed,int val){
     25     int k=++V;
     26     f[k].fa=s(0)=s(1)=0;
     27     f[k].st=st,f[k].ed=ed,f[k].val=val;
     28     if (!val)f[k].lim=f[k].mn=K+1;
     29     else{
     30         f[k].lim=f[k].mn=get_max(st,ed);
     31         add(val);
     32     }
     33     return k;
     34 }
     35 void up(int k){
     36     f[k].mn=min(min(f[s(0)].mn,f[s(1)].mn),f[k].lim);
     37 }
     38 void connect(int k,int p,int u){
     39     f[u].fa=k;
     40     if (k){
     41         s(p)=u;
     42         up(k);
     43     }
     44 }
     45 void rotate(int k){
     46     int fa=f[k].fa,ga=f[fa].fa,p=pd(k);
     47     connect(ga,pd(fa),k);
     48     connect(fa,p,s(p^1));
     49     connect(k,(p^1),fa);
     50 }
     51 void splay(int k,int fa){
     52     while (f[k].fa!=fa){
     53         int i=f[k].fa;
     54         if (f[i].fa!=fa){
     55             if (pd(k)==pd(i))rotate(i);
     56             else rotate(k);
     57         }
     58         rotate(k);
     59     }
     60 }
     61 int find1(int &k,int x){
     62     int i=k,pos=0;
     63     while (i){
     64         if (f[i].st>x)i=f[i].ch[0];
     65         else{
     66             pos=i;
     67             i=f[i].ch[1];
     68         }
     69     }
     70     if (i){
     71         splay(i,f[k].fa);
     72         k=i;
     73     }
     74     return pos;
     75 }
     76 int find2(int &k,int x){
     77     int i=k,pos=0;
     78     while (i){
     79         if (f[i].st<=x)i=f[i].ch[1];
     80         else{
     81             pos=i;
     82             i=f[i].ch[0];
     83         }
     84     }
     85     if (i){
     86         splay(i,f[k].fa);
     87         k=i;
     88     }
     89     return pos;
     90 }
     91 void add(int &k,int x){
     92     int p=find1(k,f[x].st);
     93     if (!p){
     94         connect(x,1,k);
     95         k=x;
     96     }
     97     else{
     98         splay(p,f[k].fa);
     99         k=p;
    100         if (s(1))connect(x,1,s(1));
    101         connect(k,1,x);
    102     }
    103 }
    104 void del(int &k,int x){
    105     splay(x,0);
    106     k=x;
    107     dec(f[k].val);
    108     f[k].val=0;
    109     f[k].lim=K+1;
    110     up(k);
    111 }
    112 int main(){
    113     f[0].val=f[0].mn=0x3f3f3f3f;
    114     scanf("%d%d",&n,&m);
    115     for(int i=2;i<=n;i++)Log[i]=Log[i>>1]+1;
    116     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    117     K=30;
    118     for(int i=1;i<=n;i++)
    119         if (a[i]>K)v.push_back(i);
    120     for(int i=n;i;i--){
    121         ST[i][0]=a[i];
    122         for(int j=1;j<=20;j++)ST[i][j]=max(ST[i][j-1],ST[min(i+(1<<j-1),n+1)][j-1]);
    123     }
    124     for(int i=1;i<=K;i++)rt[i]=New(1,n,0);
    125     for(int i=1;i<=m;i++){
    126         scanf("%d%d%d",&l,&r,&x);
    127         int ll=lower_bound(v.begin(),v.end(),l)-v.begin();
    128         int rr=upper_bound(v.begin(),v.end(),r)-v.begin()-1;
    129         for(int j=ll;j<=rr;j++){
    130             add(x);
    131             q[j].push(x);
    132             if (q[j].size()>a[v[j]]){
    133                 dec(q[j].front());
    134                 q[j].pop();
    135             }
    136         }
    137         rt[0]=New(l,r,x);
    138         for(int j=1;j<=K;j++){
    139             int p1=find1(rt[j],l-1);
    140             if ((p1)&&(l<=f[p1].ed)){
    141                 if (get_max(l,f[p1].ed)<j)add(rt[j],New(l,f[p1].ed,0));
    142                 else add(rt[j],New(l,f[p1].ed,f[p1].val));
    143                 splay(p1,0);
    144                 f[p1].ed=l-1;
    145                 rt[j]=p1;
    146                 if (f[p1].val){
    147                     f[p1].lim=get_max(f[p1].st,f[p1].ed);
    148                     up(p1);
    149                     if (f[p1].lim<j)del(rt[j],p1);
    150                 }
    151             }
    152             int p2=find1(rt[j],r);
    153             if ((r<n)&&(r<f[p2].ed)){
    154                 if (get_max(r+1,f[p2].ed)<j)add(rt[j],New(r+1,f[p2].ed,0));
    155                 else add(rt[j],New(r+1,f[p2].ed,f[p2].val));
    156                 splay(p2,0);
    157                 f[p2].ed=r;
    158                 rt[j]=p2;
    159                 if (f[p2].val){
    160                     f[p2].lim=get_max(f[p2].st,f[p2].ed);
    161                     up(p2);
    162                     if (f[p2].lim<j)del(rt[j],p2);
    163                 }
    164             }
    165             p2=find2(rt[j],r);
    166             if (!p1){
    167                 if (!p2)swap(rt[0],rt[j]);
    168                 else{
    169                     splay(p2,0);
    170                     rt[j]=p2;
    171                     int k=f[rt[j]].ch[0];
    172                     connect(0,0,k);
    173                     connect(rt[j],0,rt[0]);
    174                     rt[0]=k;
    175                 }
    176             }
    177             else{
    178                 splay(p1,0);
    179                 rt[j]=p1;
    180                 if (!p2){
    181                     int k=f[rt[j]].ch[1];
    182                     connect(0,0,k);
    183                     connect(rt[j],1,rt[0]);
    184                     rt[0]=k;
    185                 }
    186                 else{
    187                     splay(p2,rt[j]);
    188                     int k=f[p2].ch[0];
    189                     connect(0,0,k);
    190                     connect(p2,0,rt[0]);
    191                     rt[0]=k;
    192                     up(rt[j]);
    193                 }
    194             }
    195             while ((rt[0])&&(f[rt[0]].mn==j)){
    196                 int k=rt[0];
    197                 while (f[k].lim!=j){
    198                     if (f[s(0)].mn==j)k=s(0);
    199                     else k=s(1);
    200                 }
    201                 del(rt[0],k);
    202             }
    203         }
    204         printf("%d
    ",ans);
    205     }
    206 } 
    View Code

    2.做法2

    基本思路

    对于第$i$个操作,称其影响操作$j$当且仅当第$j$次操作结束后,第$i$次操作插入的数字还在队列中

    其所影响的操作是一个以$i$为左端点的连续区间,以下记作$[i,end_{i}]$,而当我们(离线)求出这个区间,简单差分一下就可以$o(n)$求出最终答案,因此以下考虑如何求$end_{i}$

    先考虑一个更简单的问题,如何判定$i$是否影响$j$(其中$ile j$),有如下的暴力做法:

    1.$forall i+1le kle j$,对序列$a_{i}$的$[l_{k},r_{k}]$区间减1(从初始的$a$序列开始操作)

    2.求出序列$a_{i}$的$[l_{i},r_{i}]$的区间最大值,若大于0即影响操作$j$(否则不影响)

    区间减1以及区间最大值用线段树来维护,复杂度即为$o(n^{2}log^{2}n)$,但无法通过

    分块优化

    考虑分块,设块大小为$K$,将过程分为以下两部分:

    1.确定$end_{i}$在哪一个块中

    这可以通过对每一个块首(记作$st$)出发向前遍历,遍历过程中维护线段树,因此遍历到$i$即可判定$i$是否影响$st$,对于$i$影响的$st$中最大的$st$所在块即为$end_{i}$所在块

    (特别的,若不存在则$end_{i}$在$i$所在块)

    由于每一个块首遍历都要$o(nlog n)$的复杂度,总复杂度即$o(frac{n^{2}}{K}log n)$

    2.求出$end_{i}$具体的位置

    先枚举块,然后将$end_{i}$在该块中的询问取出,并从大到小排序

    仍然从块首向前遍历,当遍历到的位置的答案在这个块中,将右端点向右移动,直至未被影响或到达块尾,即可求出该$end_{i}$,然后再将右端点移回块首

    每一个操作复杂度为$o(Klog n)$(块首遍历与之前相同,不考虑),总复杂度即$(Knlog n)$

    综上,复杂度为$o(frac{n^{2}}{K}log n+Knlog n)$,取$K=sqrt{n}$即可做到$o(nsqrt{n}log n)$的复杂度

    代码

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define N 100005
     4 #define L (k<<1)
     5 #define R (L+1)
     6 #define mid (l+r>>1)
     7 struct Operator{
     8     int l,r,x;
     9 }op[N];
    10 vector<int>v[N],v_add[N],v_dec[N];
    11 int K,n,m,ans_tot,a[N],bl[N],st[N],ed[N],f[N<<2],tag[N<<2],ans[N],tot[N],true_ans[N];
    12 void add(int k){
    13     if (++tot[k]==1)ans_tot++;
    14 }
    15 void dec(int k){
    16     if (--tot[k]==0)ans_tot--;
    17 }
    18 void upd(int k,int x){
    19     tag[k]+=x,f[k]+=x;
    20 }
    21 void down(int k){
    22     if (tag[k]){
    23         upd(L,tag[k]);
    24         upd(R,tag[k]);
    25         tag[k]=0;
    26     }
    27 }
    28 void build(int k,int l,int r){
    29     tag[k]=0;
    30     if (l==r){
    31         f[k]=a[l];
    32         return;
    33     }
    34     build(L,l,mid);
    35     build(R,mid+1,r);
    36     f[k]=max(f[L],f[R]);
    37 }
    38 void update(int k,int l,int r,int x,int y,int z){
    39     if ((l>y)||(x>r))return;
    40     if ((x<=l)&&(r<=y)){
    41         upd(k,z);
    42         return;
    43     }
    44     down(k);
    45     update(L,l,mid,x,y,z);
    46     update(R,mid+1,r,x,y,z);
    47     f[k]=max(f[L],f[R]);
    48 }
    49 int query(int k,int l,int r,int x,int y){
    50     if ((l>y)||(x>r))return f[0];
    51     if ((x<=l)&&(r<=y))return f[k];
    52     down(k);
    53     return max(query(L,l,mid,x,y),query(R,mid+1,r,x,y));
    54 }
    55 int main(){
    56     f[0]=-0x3f3f3f3f;
    57     scanf("%d%d",&n,&m);
    58     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    59     for(int i=1;i<=m;i++)scanf("%d%d%d",&op[i].l,&op[i].r,&op[i].x);
    60     K=(int)sqrt(m);
    61     for(int i=1;i<=m;i++)bl[i]=(i-1)/K+1;
    62     for(int i=1;i<=bl[m];i++){
    63         st[i]=(i-1)*K+1;
    64         ed[i]=min(i*K,m);
    65     }
    66     for(int i=1;i<=bl[m];i++){
    67         build(1,1,n);
    68         for(int j=ed[i];j;j--){
    69             if (query(1,1,n,op[j].l,op[j].r)>0)ans[j]=i;
    70             if (j<=st[i])update(1,1,n,op[j].l,op[j].r,-1);
    71         }
    72     }
    73     for(int i=1;i<=m;i++)v[ans[i]].push_back(i);
    74     for(int i=1;i<=bl[m];i++){
    75         build(1,1,n);
    76         for(int j=ed[i];v[i].size();j--){
    77             if (v[i].back()==j){
    78                 for(ans[j]=max(st[i],j);ans[j]<ed[i];ans[j]++){
    79                     update(1,1,n,op[ans[j]+1].l,op[ans[j]+1].r,-1);
    80                     if (query(1,1,n,op[j].l,op[j].r)<=0)break;
    81                 }
    82                 for(int k=max(st[i],j);(k<=ans[j])&&(k<ed[i]);k++)update(1,1,n,op[k+1].l,op[k+1].r,1);
    83                 v[i].pop_back();
    84             }
    85             if (j<=st[i])update(1,1,n,op[j].l,op[j].r,-1);
    86         }
    87     }
    88     for(int i=1;i<=m;i++){
    89         v_add[i].push_back(op[i].x);
    90         v_dec[ans[i]].push_back(op[i].x);
    91     }
    92     for(int i=1;i<=m;i++){
    93         for(int j=0;j<v_add[i].size();j++)add(v_add[i][j]);
    94         printf("%d
    ",ans_tot);
    95         for(int j=0;j<v_dec[i].size();j++)dec(v_dec[i][j]);
    96     }
    97 } 
    View Code

    3.做法3

    基本思路

    如果可以证明$forall 1le i<m,end_{i}le end_{i+1}$,根据单调性,可以在$o(nlog n)$的时间内求出$end_{i}$

    但这件事情显然是错误的,不过若第$i$次操作和第$j$次操作的区间相同($i<j$),则$end_{i}le end_{j}$

    由此,我们想到将一个操作的区间拆开,并对相同的区间用单调性来做

    考虑分块,同样以$K$为大小进行分块,将每一个操作拆为若干个整块操作和至多两个非整块操作,那么对于操作$i$的$end_{i}$,也就是所有拆出的操作的$end$的最大值

    由于一个操作至多影响一个块,根据两个块之间的独立性,将每一个块的操作分别处理

    具体来说,这个块内的操作分为整块操作和非整块操作,以下分别来处理

    整块操作

    先来说一下关于复杂度的描述:

    记$n_{1}$为块内整块操作数,$n_{2}$为非整块操作数,用$n_{1}$和$n_{2}$来描述块内操作复杂度,并根据$sum n_{1}=o(frac{n^{2}}{K})$和$sum n_{2}=o(n)$来求出总时间复杂度

    对于整块操作,也就是操作区间相同,即可以根据单调性来做,块内复杂度为$o((n_{1}+n_{2})log n)$,总时间复杂度即$o(frac{n^{2}}{K}log n)$

    考虑优化,对整块操作直接在线段树外打懒标记即可,这样块内复杂度即降为$o(n_{1}+n_{2}log n)$,累加后也就是$o(frac{n^{2}}{K}+nlog n)$,可以忽略$o(nlog n)$,即$o(frac{n^{2}}{K})$

    另外,我们以此法找到的是块内的$end_{i}$,如果算上块外,应该取块内下一个操作(如果不存在则可以忽略或设置为$m+1$)减1作为其$end_{i}$,下面非整块操作相同

    非整块操作

    我们将这些操作再继续拆分,拆分为$K$个操作,这些操作同样可以用单调性来做

    但这里的判定与之前不同,我们要加上我们所忽略了的整块操作,通过前缀和+差分来求出区间内的整块操作数即可计算

    对于一个队列显然就不需要线段树了,复杂度为$o(Kn_{2}+n)$(这个$n$是前缀和+差分的复杂度)

    我们现在对于$i$操作所找到的非整块操作,实际上是在$end_{i}$之前(包括$end_{i}$自身)第一个非整块操作,通过预处理每一个第$i$个整块操作以及之前的前缀和,可以$o(1)$找到$end_{i}$

    综上,块内复杂度为$o(Kn_{2}+n)$,总复杂度为$o(Kn+frac{n^{2}}{K})$

    综合整块操作,复杂度仍然是$o(Kn+frac{n^{2}}{K})$,取$K=sqrt{n}$可做到$o(nsqrt{n})$的复杂度

    (另外,由于空间问题,需要将每一次继续拆分的$K$个操作所记录到的$K$个vector来重复利用)

    代码

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define N 100005
      4 #define L (k<<1)
      5 #define R (L+1)
      6 #define mid (l+r>>1)
      7 struct Operator{
      8     int l,r,x;
      9 };
     10 vector<Operator>op[N];
     11 vector<int>v_all,v[N],v_add[N],v_dec[N];
     12 int K,n,m,l,r,ans_tot,a[N],bl[N],st[N],ed[N],val[N],sum[N],f[N<<2],tag[N<<2],ans[N],tot[N];
     13 void add(int k){
     14     if (++tot[k]==1)ans_tot++;
     15 }
     16 void dec(int k){
     17     if (--tot[k]==0)ans_tot--;
     18 }
     19 void upd(int k,int x){
     20     tag[k]+=x,f[k]+=x;
     21 }
     22 void down(int k){
     23     if (tag[k]){
     24         upd(L,tag[k]);
     25         upd(R,tag[k]);
     26         tag[k]=0;
     27     }
     28 }
     29 void build(int k,int l,int r){
     30     tag[k]=0;
     31     if (l==r){
     32         f[k]=a[l];
     33         return;
     34     }
     35     build(L,l,mid);
     36     build(R,mid+1,r);
     37     f[k]=max(f[L],f[R]);
     38 }
     39 void update(int k,int l,int r,int x,int y,int z){
     40     if ((l>y)||(x>r))return;
     41     if ((x<=l)&&(r<=y)){
     42         upd(k,z);
     43         return;
     44     }
     45     down(k);
     46     update(L,l,mid,x,y,z);
     47     update(R,mid+1,r,x,y,z);
     48     f[k]=max(f[L],f[R]);
     49 }
     50 int main(){
     51     f[0]=-0x3f3f3f3f;
     52     scanf("%d%d",&n,&m);
     53     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
     54     K=(int)sqrt(n);
     55     for(int i=1;i<=n;i++)bl[i]=(i-1)/K+1;
     56     for(int i=1;i<=bl[n];i++){
     57         st[i]=(i-1)*K+1;
     58         ed[i]=min(i*K,n);
     59     }
     60     for(int i=1;i<=m;i++){
     61         scanf("%d%d%d",&l,&r,&val[i]);
     62         if (bl[l]==bl[r])op[bl[l]].push_back(Operator{l,r,i});
     63         else{
     64             op[bl[l]].push_back(Operator{l,ed[bl[l]],i});
     65             op[bl[r]].push_back(Operator{st[bl[r]],r,i});
     66             for(int j=bl[l]+1;j<bl[r];j++)op[j].push_back(Operator{st[j],ed[j],i});
     67         }
     68     }
     69     for(int i=1;i<=bl[n];i++){
     70         build(1,st[i],ed[i]);
     71         v_all.clear();
     72         memset(sum,0,sizeof(sum));
     73         for(int j=0;j<=ed[i]-st[i];j++)v[j].clear();
     74         for(int j=0,k=0;j<op[i].size();j++){
     75             if (j)update(1,st[i],ed[i],op[i][j].l,op[i][j].r,1);
     76             if ((op[i][j].l==st[i])&&(op[i][j].r==ed[i])){
     77                 v_all.push_back(op[i][j].x);
     78                 sum[op[i][j].x]++;
     79                 while ((k<op[i].size())&&(f[1]>0)){
     80                     k++;
     81                     if (k<op[i].size())update(1,st[i],ed[i],op[i][k].l,op[i][k].r,-1);
     82                 }
     83                 if (k==op[i].size())ans[op[i][j].x]=m;
     84                 else ans[op[i][j].x]=max(ans[op[i][j].x],op[i][k].x-1);
     85             }
     86             else{
     87                 for(int t=op[i][j].l;t<=op[i][j].r;t++)v[t-st[i]].push_back(op[i][j].x);
     88             }
     89         }
     90         for(int j=1;j<m;j++)sum[j+1]+=sum[j];
     91         for(int j=0;j<=ed[i]-st[i];j++){
     92             int lim=a[j+st[i]];
     93             for(int k=0,t=0;k<v[j].size();k++){
     94                 while ((t<v[j].size())&&(t-k+sum[v[j][t]]-sum[v[j][k]]<lim))t++;
     95                 if (t<v[j].size()){
     96                     if (t-k+sum[v[j][t]]-sum[v[j][k]]==lim)ans[v[j][k]]=max(ans[v[j][k]],v[j][t]-1);
     97                     else ans[v[j][k]]=max(ans[v[j][k]],v_all[lim+sum[v[j][k]]-t+k]-1);
     98                 }
     99                 else{
    100                     if (t-k+sum[m]-sum[v[j][k]]<=lim)ans[v[j][k]]=m;
    101                     else ans[v[j][k]]=max(ans[v[j][k]],v_all[lim+sum[v[j][k]]-t+k]-1);
    102                 }
    103             }
    104         }
    105     }
    106     for(int i=1;i<=m;i++){
    107         v_add[i].push_back(val[i]);
    108         v_dec[ans[i]].push_back(val[i]);
    109     }
    110     for(int i=1;i<=m;i++){
    111         for(int j=0;j<v_add[i].size();j++)add(v_add[i][j]);
    112         printf("%d
    ",ans_tot);
    113         for(int j=0;j<v_dec[i].size();j++)dec(v_dec[i][j]);
    114     }
    115 } 
    View Code

    线段树分治

    事实上,这道题还可以做到更好的理论时间复杂度

    考虑将区间用线段树去划分(类似于线段树分治的写法),用类似地方法处理,具体来说如下:

    1.对于之前的整块操作,不能前缀和+差分以及暴力记录来查找,需要另外建立一棵线段树,并再返回时撤销即可,这里修改总复杂度为$o(nlog^{2}n)$,询问单次$o(log n)$(包括求和以及查找)

    2.对于每一个节点,将子树内所有操作都放在自己上并求出所有恰好覆盖该块,注意到这等价于线段树区间修改的复杂度,因此操作总量是$o(nlog n)$的,每一次需要修改限制以及在上面线段树查找,也是$o(nlog^{2}n)$

    综上,我们得到了一个理论复杂度$o(nlog^{2}n)$,但其实际运行时间远劣于上面的分块

    代码

      1 #include<bits/stdc++.h>
      2 using namespace std;
      3 #define N 100005
      4 #define L (k<<1)
      5 #define R (L+1)
      6 #define mid (l+r>>1)
      7 struct Operator{
      8     int l,r,x;
      9 };
     10 vector<Operator>op[N<<2];
     11 vector<int>v_add[N],v_dec[N];
     12 int n,m,l,r,ans_tot,a[N],val[N],lim[N<<2],tag[N<<2],sum[N<<2],ans[N],tot[N];
     13 void add(int k){
     14     if (++tot[k]==1)ans_tot++;
     15 }
     16 void dec(int k){
     17     if (--tot[k]==0)ans_tot--;
     18 }
     19 void upd(int k,int x){
     20     tag[k]+=x,lim[k]+=x;
     21 }
     22 void down(int k){
     23     if (tag[k]){
     24         upd(L,tag[k]);
     25         upd(R,tag[k]);
     26         tag[k]=0;
     27     }
     28 }
     29 void build(int k,int l,int r){
     30     tag[k]=0;
     31     if (l==r){
     32         lim[k]=a[l];
     33         return;
     34     }
     35     build(L,l,mid);
     36     build(R,mid+1,r);
     37     lim[k]=max(lim[L],lim[R]);
     38 }
     39 void update_lim(int k,int l,int r,int x,int y,int z){
     40     if ((l>y)||(x>r))return;
     41     if ((x<=l)&&(r<=y)){
     42         upd(k,z);
     43         return;
     44     }
     45     down(k);
     46     update_lim(L,l,mid,x,y,z);
     47     update_lim(R,mid+1,r,x,y,z);
     48     lim[k]=max(lim[L],lim[R]);
     49 }
     50 void update_cover(int k,int l,int r,int x,int y){
     51     sum[k]+=y;
     52     if (l==r)return;
     53     if (x<=mid)update_cover(L,l,mid,x,y);
     54     else update_cover(R,mid+1,r,x,y);
     55 }
     56 int query(int k,int l,int r,int x,int y){
     57     if ((l>y)||(x>r))return 0;
     58     if ((x<=l)&&(r<=y))return sum[k];
     59     return query(L,l,mid,x,y)+query(R,mid+1,r,x,y);
     60 }
     61 int find(int k,int l,int r,int x){
     62     if (l==r)return l;
     63     if (x<=sum[L])return find(L,l,mid,x);
     64     return find(R,mid+1,r,x-sum[L]);
     65 }
     66 void add(int k,int l,int r,int x,int y,int z){
     67     if ((l>y)||(x>r))return;
     68     op[k].push_back(Operator{max(l,x),min(r,y),z});
     69     if ((x<=l)&&(r<=y))return;
     70     add(L,l,mid,x,y,z);
     71     add(R,mid+1,r,x,y,z);
     72 }
     73 void dfs(int k,int l,int r){
     74     build(1,l,r);
     75     for(int i=0,j=0;i<op[k].size();i++){
     76         int t=op[k][i].x;
     77         if (i)update_lim(1,l,r,op[k][i].l,op[k][i].r,1);
     78         if ((op[k][i].l==l)&&(op[k][i].r==r)){
     79             while ((j<op[k].size())&&(lim[1]>query(1,1,m,t,op[k][j].x))){
     80                 j++;
     81                 if (j<op[k].size())update_lim(1,l,r,op[k][j].l,op[k][j].r,-1);
     82             }
     83             if (j==op[k].size()){
     84                 if (query(1,1,m,t,m)<lim[1])ans[t]=m;
     85                 else ans[t]=max(ans[t],find(1,1,m,lim[1]+query(1,1,m,1,t))-1);
     86             }
     87             else{
     88                 update_lim(1,l,r,op[k][j].l,op[k][j].r,1);
     89                 if (query(1,1,m,t,op[k][j].x)<lim[1])ans[t]=max(ans[t],op[k][j].x-1);
     90                 else ans[t]=max(ans[t],find(1,1,m,lim[1]+query(1,1,m,1,t))-1);
     91                 update_lim(1,l,r,op[k][j].l,op[k][j].r,-1);
     92             }
     93         }
     94     }
     95     for(int i=0;i<op[k].size();i++)
     96         if ((op[k][i].l==l)&&(op[k][i].r==r))update_cover(1,1,m,op[k][i].x,1);
     97     if (l<r){
     98         dfs(L,l,mid);
     99         dfs(R,mid+1,r);
    100     }
    101     for(int i=0;i<op[k].size();i++)
    102         if ((op[k][i].l==l)&&(op[k][i].r==r))update_cover(1,1,m,op[k][i].x,-1);
    103 }
    104 int main(){
    105     scanf("%d%d",&n,&m);
    106     for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    107     for(int i=1;i<=m;i++){
    108         scanf("%d%d%d",&l,&r,&val[i]);
    109         add(1,1,n,l,r,i);
    110     }
    111     dfs(1,1,n);
    112     for(int i=1;i<=m;i++){
    113         v_add[i].push_back(val[i]);
    114         v_dec[ans[i]].push_back(val[i]);
    115     }
    116     for(int i=1;i<=m;i++){
    117         for(int j=0;j<v_add[i].size();j++)add(v_add[i][j]);
    118         printf("%d
    ",ans_tot);
    119         for(int j=0;j<v_dec[i].size();j++)dec(v_dec[i][j]);
    120     }
    121 }
    View Code
  • 相关阅读:
    在Android Studio中用Gradle添加Robolectric
    Windows环境下利用github快速配置git环境
    上传Android代码到Jcenter(解决了字符映射的问题)
    RecyclerView和ScrollView嵌套使用
    Android Java类编写规范+优化建议
    3.0之后在LinearLayout里增加分割线
    【转】Android Studio中通过快捷键来提取提取方法
    为什么Android应该根据屏幕分辨率来加载不同的图片文件
    JSON/XML格式化插件比较
    利用在线工具根据JSon数据自动生成对应的Java实体类
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/14511385.html
Copyright © 2020-2023  润新知