P11286 [COTS 2017] 盗道 Krimošten 题解

xixisuper

2024-11-18 14:40:47

Solution

P11286 [COTS 2017] 盗道 Krimošten 题解

小清新数据结构题,感觉没有紫。

思路

先来想弱化版的情况:假设我每次询问的 $[l,r]$ 都是 $[1,n]$,但是 $c$ 不一样,怎么做? 一个比较显然的思路是我直接维护一个值域,位置 $j$ 上的数表示初始时盗贼有 $j$ 库纳,在当前情况下有多少库纳,初始化的时候位置 $j$ 上的数就等于 $j$。对于每一户人家 $i$,**找到当前情况下值小于 $a_i$ 的那些盗贼,给他们全部加 $1$**;**找到当前情况下值大于 $a_i$ 的那些盗贼,给他们全部减 $1$**。最后,第 $j$ 个位置上的数就是对应的解。 不难发现上述算法的时间复杂度瓶颈在于如何进行快速的加减 $1$ 操作。我们能够发现,**盗贼当前的库那个数随初始库那个数单调不降**。证明这个也很简单,你会发现对于 $x<y$ 来说,在经过若干次操作后只可能会达到 $x=y$ 的一个状态,往后的所有人家都会使 $x,y$ 同加同减,所以 $x$ 永远小于等于 $y$,单调性得证。 所以我们**在值域上加的盗贼一定是一段前缀,减的盗贼一定是一段后缀**,找 $a_i$ 的位置可以用线段树上二分解决,而加减 $1$ 的操作线段树也支持,所以能够做到 $O(n\log V)$ 解决该问题。 现在来想一般情况,不难发现我们只需要把上述问题进行一个拓展就行。我们考虑**把所有询问离线,套用扫描线的思路,在遍历到第 $i$ 户人家前,把所有左端点 $L=i$ 的那些询问记录下 $c$ 在值域中出现的位置;在计算完第 $i$ 户人家的贡献后,把所有 $R=i$ 的询问的答案记录下来,最后输出即可。** 注意在这种实现下,最开始时值域的范围是 $[-n,V+n]$,因为我们要保证在扫描线时,$[0,V]$ 以内的所有数都出现在值域里,而操作 $n$ 户人家至多使最小值增加 $n$,使最大值减小 $n$。 归到时间复杂度分析,瓶颈在于线段树扫描线,时间复杂度为 $O((n+m)\log (n+V))$。 ## 代码 毫无聪明的实现,看看就好。 ```cpp #include <iostream> #include <vector> #include <queue> #include <algorithm> #define ll int #define lc (x<<1) #define rc ((x<<1)|1) #define mid ((l+r)>>1) using namespace std; const ll V=1e6+10; const ll N=5e5+10; const ll MX=2*V-5; ll tag[MX<<3],mx[MX<<3],a[N],chk[MX],n,m; struct node{ ll L,R,c,ans,id,pos; friend bool operator >(const node x,const node y){ return x.R>y.R; } }ask[N],ot[N]; priority_queue<node,vector<node>,greater<node> > qu; bool cmp1(node x,node y){return x.L<y.L;} bool cmp2(node x,node y){return x.id<y.id;} void push_up(ll x){mx[x]=max(mx[lc],mx[rc]);} void build(ll x,ll l,ll r){ if(l==r){ mx[x]=chk[l]; return; } build(lc,l,mid); build(rc,mid+1,r); push_up(x); } void push_down(ll x){ if(!tag[x]) return; tag[lc]+=tag[x],tag[rc]+=tag[x]; mx[lc]+=tag[x],mx[rc]+=tag[x]; tag[x]=0; } void add(ll x,ll l,ll r,ll L,ll R,ll p){ if(L<=l&&r<=R){ tag[x]+=p; mx[x]+=p; return; } push_down(x); if(L<=mid) add(lc,l,mid,L,R,p); if(R>mid) add(rc,mid+1,r,L,R,p); push_up(x); } ll gtpos(ll x,ll l,ll r,ll k){ if(l==r) return l; push_down(x); if(mx[lc]>=k) return gtpos(lc,l,mid,k); return gtpos(rc,mid+1,r,k); } ll gtval(ll x,ll l,ll r,ll pos){ if(l==r) return mx[x]; push_down(x); if(pos<=mid) return gtval(lc,l,mid,pos); return gtval(rc,mid+1,r,pos); } int main(){ ios::sync_with_stdio(false); cin.tie(0);cout.tie(0); cin>>n>>m; for(ll i=1;i<=n;i++) cin>>a[i]; for(ll i=1;i<=m;i++){ cin>>ask[i].L>>ask[i].R>>ask[i].c; ask[i].id=i; } sort(ask+1,ask+1+m,cmp1); for(ll i=1;i<=MX;i++) chk[i]=i-N; build(1,1,MX); ll top=1,as=1; for(ll i=1;i<=n;i++){ while(top<=m&&ask[top].L==i){ ask[top].pos=gtpos(1,1,MX,ask[top].c); qu.push(ask[top]); top++; } ll pls=gtpos(1,1,MX,a[i])-1,minu=gtpos(1,1,MX,a[i]+1); add(1,1,MX,1,pls,1),add(1,1,MX,minu,MX,-1); while(!qu.empty()&&qu.top().R==i){ ot[as]=qu.top(); qu.pop(); ot[as].ans=gtval(1,1,MX,ot[as].pos); as++; } } sort(ot+1,ot+1+m,cmp2); for(ll i=1;i<=m;i++) cout<<ot[i].ans<<"\n"; return 0; } ```