[2018 集训队互测 Day 1] 完美的队列
一、题目
二、解法
突破本题的关键是全局询问,对于每个询问 \((l,r,x)\),考虑计算出它完全消失的时间 \(ed_i\),那么在 \([i,ed_i)\) 这段时间内权值 \(x\) 都是出现的。所以如果我们处理出了所有 \(ed_i\),可以直接回答询问。
处理 \(ed_i\) 并不好直接 polylog
,考虑将原序列分块。那么每个询问会被拆分成若干个整块和散块,我们分别处理整块和散块对询问的贡献即可。(贡献的含义是,把被弹出的时间拿给询问取最大值)
考虑整块对询问的贡献,直接对所有询问 two-pointers
,我们维护当前局面最大的 \(a_i\) 记为 \(mx\),再维护一个整体覆盖次数记为 \(tag\),那么如果 \(mx>tag\) 就右移右端点。
加入一个询问时,如果它覆盖了整个块,那么直接修改 \(tag\),否则暴力修改这个块的一部分,暴力重新计算 \(mx\),移动左端点时删除询问。由于所有快重构的总次数不超过 \(O(m)\),均摊下来复杂度 \(O(m\sqrt n)\)
考虑散块对询问的贡献,我们把在这个块内拥有散块的询问取出来,称这种询问为 \(z\) 询问。先枚举块内的单点,然后直接对 \(z\) 询问 two-pointers
(必须要把这个单点弹出),需要预处理这些东西:
- 前 \(i\) 个询问覆盖整个块的总次数 \(pre_i\)
- 第 \(i\) 个 \(z\) 询问的上一个覆盖整个块的询问编号 \(lst_i\)
因为右端点不一定是 \(z\) 询问,可能是一个覆盖整个块的询问,这可以通过分类讨论解决。
时间复杂度 \(O(m\sqrt n)\)
#include <cstdio>
#include <vector>
#include <iostream>
#include <cmath>
using namespace std;
const int M = 100005;
int read()
{
int x=0,f=1;char c;
while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
return x*f;
}
int n,m,k,ans,a[M],b[M],l[M],r[M],x[M],ed[M];
int mx,tg,L,R,c[M],d[M],pre[M],lst[M],cnt[M];
vector<int> v[M];
void get()
{
mx=-1e9;
for(int i=L;i<=R;i++) mx=max(mx,b[i]);
}
void add(int l,int r,int c)
{
if(l<=L && R<=r) {tg+=c;return ;}
if(L>r || l>R) return ;
for(int i=max(l,L);i<=min(r,R);i++)
b[i]+=c;
get();
}
void work()
{
int t=0,o=0;get();tg=0;
for(int i=1,j=2;i<=m;i++)
{
while(j<=m && mx+tg>0)
add(l[j],r[j],-1),j++;
pre[i]=pre[i-1];
if(l[i]<=L && R<=r[i])
{
ed[i]=max(ed[i],mx+tg>0?m+1:j-1);
c[++t]=i;pre[i]++;
}
else if(L<=r[i] && l[i]<=R)
d[++o]=i,lst[o]=t;
add(l[i+1],r[i+1],1);
}
for(int i=L;i<=R;i++)
{
tg=a[i];
for(int j=1,k=1;j<=o;j++)
{
while(k<o && tg>0)
k++,tg-=pre[d[k]]-pre[d[k-1]]+
(l[d[k]]<=i && i<=r[d[k]]);
if(l[d[j]]<=i && i<=r[d[j]])
{
if(tg<=0)
{
int f=l[d[k]]<=i && i<=r[d[k]];
if(f && tg==0)
ed[d[j]]=max(ed[d[j]],d[k]);
else
ed[d[j]]=max(ed[d[j]],c[lst[k]+tg+f]);
}
else
{
if(tg<=pre[m]-pre[d[o]])
ed[d[j]]=max(ed[d[j]],c[lst[o]+tg]);
else
ed[d[j]]=m+1;
}
}
tg+=pre[d[j+1]]-pre[d[j]]+
(l[d[j+1]]<=i && i<=r[d[j+1]]);
}
}
}
signed main()
{
n=read();m=read();k=sqrt(n);
for(int i=1;i<=n;i++) a[i]=b[i]=read();
for(int i=1;i<=m;i++)
l[i]=read(),r[i]=read(),x[i]=read();
for(int T=0;T*k<n;T++)
L=T*k+1,R=min(n,L+k-1),work();
for(int i=1;i<=m;i++)
{
if(!(cnt[x[i]]++)) ans++;
v[ed[i]].push_back(x[i]);
for(int x:v[i])
if(!(--cnt[x])) ans--;
printf("%d\n",ans);
}
}