[loj2461]完美的队列
参考论文,这里一共写了论文中的3种做法,第一种做法为强制在线时的做法,第二种为时间复杂度略高的做法(前两种都无法通过),第三种为本题正解,并给出了一种理论复杂度更优的做法
1.做法1
情况1
$\forall 1\le i\le n,a_{i}=1$
此时相当于维护一个序列,要求支持区间覆盖&求值的种类数
考虑去将相邻且权值(操作编号)相同的缩为一个段,用set来维护这些段,具体来说,对于每一段维护一个起点以及权值,将其作为set中的一个二元组,按起点从小到大排序
(特别的,为了避免有空隙而无法被表示,初始假设所有元素都为0)
对于一个操作,也就是将起点在$[l,r]$中的元素删除,并加入$(l,now)$以及$(r+1,last)$即可(其中$now$指当前操作的权值,$last$指插入前起点不超过$r$且最大的二元组的权值
(若已经存在以$r+1$为起点的二元组,则不插入$(r+1,last)$)
暴力删除即可,显然由于每一次至多产生两个元素,因此总时间复杂度为$o(n\log n)$
另外,关于答案的维护,直接统计所有二元组中每一个非0权值各有几个,在修改时维护即可
情况2
$\forall 1\le i\le n,a_{i}=k$
延续之前的做法,考虑去维护$k$个set
具体来说,将所有队列中的元素按队尾对齐,然后对于纵向的每一列维护一个set,每一次本要删除的元素加入下一个set中即可(对于最后一个set直接删除)
在加入下一个set时,我们要将所有元素一起插入,即先将其作为一个整体的$[l,r]$插入下一个set中,再将其中具体的每一段插入,避免新建过多节点
时间复杂度上,即每次操作至多产生$2k$个元素,且每一个元素至多被后移$k$次,因此总复杂度为$o(k^{2}n\log n)$
这个做法可以优化,如果通过splay来手动实现set,将这个区间在splay中分离出来,并直接接入到下一个中即可,这样的删除就不是均摊,而是每一次操作严格操作$k$次,总复杂度为$o(kn\log n)$
情况3
$\forall 1\le i\le n,a_{i}\le k$
此时先将其作为$\forall 1\le i\le n,a_{i}=k$来做,并预处理ST表来支持区间最小值,当某一个区间内$a_{i}$的最大值都小于当前set的编号则将其权值改为0(统计的是非0权值)
这样我们需要对所有操作的区间挨个检验,那么之前使用splay来优化并没有意义,仍为$o(k^{2}n\log n)$
考虑对每一个二元组再加一个权值,表示其区间内$a_{i}$的最大值,并用splay维护子树内的最小值,当发现存在最小值<set的编号,则找到最小值来源并删除即可
此时,由于每一个区间在被删除时才会有贡献,总复杂度变为$o(kn\log 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(Kn\log n+\frac{10^{6}n}{K})$,取$K=\sqrt{\frac{10^{6}}{\log n}}$,复杂度即为$o(n\sqrt{10^{6}\log n})$,常数略卡
(实际情况下,由于空间问题,只能取$K=30$)
代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",ans); 205 } 206 }
2.做法2
基本思路
对于第$i$个操作,称其影响操作$j$当且仅当第$j$次操作结束后,第$i$次操作插入的数字还在队列中
其所影响的操作是一个以$i$为左端点的连续区间,以下记作$[i,end_{i}]$,而当我们(离线)求出这个区间,简单差分一下就可以$o(n)$求出最终答案,因此以下考虑如何求$end_{i}$
先考虑一个更简单的问题,如何判定$i$是否影响$j$(其中$i\le j$),有如下的暴力做法:
1.$\forall i+1\le k\le 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(n\log n)$的复杂度,总复杂度即$o(\frac{n^{2}}{K}\log n)$
2.求出$end_{i}$具体的位置
先枚举块,然后将$end_{i}$在该块中的询问取出,并从大到小排序
仍然从块首向前遍历,当遍历到的位置的答案在这个块中,将右端点向右移动,直至未被影响或到达块尾,即可求出该$end_{i}$,然后再将右端点移回块首
每一个操作复杂度为$o(K\log n)$(块首遍历与之前相同,不考虑),总复杂度即$(Kn\log n)$
综上,复杂度为$o(\frac{n^{2}}{K}\log n+Kn\log n)$,取$K=\sqrt{n}$即可做到$o(n\sqrt{n}\log n)$的复杂度
代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",ans_tot); 95 for(int j=0;j<v_dec[i].size();j++)dec(v_dec[i][j]); 96 } 97 }
3.做法3
基本思路
如果可以证明$\forall 1\le i<m,end_{i}\le end_{i+1}$,根据单调性,可以在$o(n\log 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}+n\log n)$,可以忽略$o(n\log 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(n\sqrt{n})$的复杂度
(另外,由于空间问题,需要将每一次继续拆分的$K$个操作所记录到的$K$个vector来重复利用)
代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",ans_tot); 113 for(int j=0;j<v_dec[i].size();j++)dec(v_dec[i][j]); 114 } 115 }
线段树分治
事实上,这道题还可以做到更好的理论时间复杂度
考虑将区间用线段树去划分(类似于线段树分治的写法),用类似地方法处理,具体来说如下:
1.对于之前的整块操作,不能前缀和+差分以及暴力记录来查找,需要另外建立一棵线段树,并再返回时撤销即可,这里修改总复杂度为$o(n\log^{2}n)$,询问单次$o(\log n)$(包括求和以及查找)
2.对于每一个节点,将子树内所有操作都放在自己上并求出所有恰好覆盖该块,注意到这等价于线段树区间修改的复杂度,因此操作总量是$o(n\log n)$的,每一次需要修改限制以及在上面线段树查找,也是$o(n\log^{2}n)$
综上,我们得到了一个理论复杂度$o(n\log^{2}n)$,但其实际运行时间远劣于上面的分块
代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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\n",ans_tot); 119 for(int j=0;j<v_dec[i].size();j++)dec(v_dec[i][j]); 120 } 121 }