参考论文,这里一共写了论文中的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$)

代码

  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)$的复杂度

代码

 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来重复利用)

代码

  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)$,但其实际运行时间远劣于上面的分块

代码

  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 }

[loj2461]完美的队列的更多相关文章

  1. LOJ2461 完美的队列 分块

    传送门 如果对于每一个操作\(i\)找到这个操作中所有的数都被pop掉的时间\(ed_i\),那么剩下就直接差分覆盖一下就可以了. 那么考虑如何求出\(ed_i\).发现似乎并没有什么数据结构能够维护 ...

  2. 【loj2461】【2018集训队互测Day 1】完美的队列

    #2461. 「2018 集训队互测 Day 1」完美的队列 传送门: https://loj.ac/problem/2461 题解: 直接做可能一次操作加入队列同时会弹出很多数字,无法维护:一个操作 ...

  3. 【LOJ2461】「2018 集训队互测 Day 1」完美的队列(分块+双指针)

    点此看题面 大致题意: 让你维护\(n\)个有限定长度的队列,每次区间往队列里加数,求每次加完后的队列里剩余元素种类数. 核心思路 这道题可以用分块+双指针去搞. 考虑求出每个操作插入的元素在队列中被 ...

  4. @loj - 2461@ 「2018 集训队互测 Day 1」完美的队列

    目录 @description@ @solution@ @part - 0@ @part - 1@ @accepted code@ @details@ @description@ 小 D 有 n 个 ...

  5. nmq消息队列解析

    消息中间件NMQ 1.What is nmq? nmq = new message queue; 一个通用消息队列系统 为在线服务设计 什么是消息队列?问什么需要?有哪些功能? 消息队列的本质:1.多 ...

  6. TCP带外数据

    传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据,如果通信一方有重要的数据需要通知对方时,协议能够将这些数据快速地发送到对方.为了发送这些数据,协议一般不使用与普通数据相同 ...

  7. 完美字符子串 单调队列预处理+DP线段树优化

    题意:有一个长度为n的字符串,每一位只会是p或j.你需要取出一个子串S(注意不是子序列),使得该子串不管是从左往右还是从右往左取,都保证每时每刻已取出的p的个数不小于j的个数.如果你的子串是最长的,那 ...

  8. Netty构建分布式消息队列实现原理浅析

    在本人的上一篇博客文章:Netty构建分布式消息队列(AvatarMQ)设计指南之架构篇 中,重点向大家介绍了AvatarMQ主要构成模块以及目前存在的优缺点.最后以一个生产者.消费者传递消息的例子, ...

  9. 体验Rabbitmq强大的【优先级队列】之轻松面对现实业务场景

    说到队列的话,大家一定不会陌生,但是扯到优先级队列的话,还是有一部分同学是不清楚的,可能是不知道怎么去实现吧,其实呢,,,这东西已 经烂大街了...很简单,用“堆”去实现的,在我们系统中有一个订单催付 ...

随机推荐

  1. 微软发布了Visual Studio 2022 RC版,并将在11月8日发布正式版

    微软今天发布了Visual Studio 2022 最接近正式发布的RC版本,同时宣布在11月8日发布正式版,届时将在线上发布虚拟的发布活动,具体参见:https://devblogs.microso ...

  2. java笔记(一直更新)

    .equals()调用时,最好把equals前面放常量或者是确定有的,如果是前面是null,则会报空指针错误 也可以在调用前判断是否为null

  3. 题解 Crash 的文明世界

    题目传送门 题目大意 给出一个\(n\)个点的树,和常数\(k\),对于\(\forall i\in[1,n]\),求出: \[\sum_{j=1}^{n} \text{dist}(i,j)^k \] ...

  4. 洛谷4322 SHOI2014 三叉神经树(LCT+思维)

    好久之前做的题了QWQ 现在来补一发博客 一道神仙题啊..qwq 首先,我们可以看出来,我们如果对于每个点维护一个\(val\),表示他的直系儿子中有几个表现为1的. 那么\(val[x]>&g ...

  5. Spring Boot 如何热加载jar实现动态插件?

    一.背景 动态插件化编程是一件很酷的事情,能实现业务功能的 解耦 便于维护,另外也可以提升 可扩展性 随时可以在不停服务器的情况下扩展功能,也具有非常好的 开放性 除了自己的研发人员可以开发功能之外, ...

  6. netty系列之:让TLS支持http2

    目录 简介 TLS的扩展协议NPN和ALPN SslProvider ApplicationProtocolConfig 构建SslContext ProtocolNegotiationHandler ...

  7. linux系统(centos)下su和sudo命令的区别

    linux系统(centos)下su和sudo命令的区别 区别 我们在日常使用过程中,这2个命令很多时候能达到相同的效果,对细节区别十分模糊,这里进行简单的解释和区分.希望大家能够正确使用这2个命令, ...

  8. 在python中实现BASE64编码

    什么是Base64编码 BASE64是用于传输8Bit字节的编码方式之一,是一种基于64个可打印字符来表示二进制数据的方法. 如下是转换表:The Base64 Alphabet Base64编码可以 ...

  9. MongoDB中如何优雅地删除大量数据

    删除大量数据,无论是在哪种数据库中,都是一个普遍性的需求.除了正常的业务需求,我们需要通过这种方式来为数据库"瘦身". 为什么要"瘦身"呢? 表的数据量到达一定 ...

  10. 6. 站在巨人的肩膀学习Java Filter型内存马

    本文站在巨人的肩膀学习Java Filter型内存马,文章里面的链接以及图片引用于下面文章,参考文章: <Tomcat 内存马学习(一):Filter型> <tomcat无文件内存w ...