Luogu P8543 纯粹的复仇女神

Description

传送门

Solution

感觉不算很套路的 DS 题吧,想了 1h 才会。

考虑对每种颜色分开考虑。假设颜色 \(C\) 对应位置 \(\{p_1,p_2,\cdots,p_k\}\),考虑 \(p_k\) 何时会对询问区间 \([ql,qr]\) 产生贡献。令 \(p_i\) 之前 / 之后第一个权值 \(< / \le\) 它的是 \(L_i,R_i\),则 \(ql \in (L_i,p_i],qr \in [p_i,R_i)\)\(p_k=\arg \min_{c_i = k}\{a_i\}\) 的充要条件。

问题转化为:\(n\) 次矩形取 \(\max\)\(q\) 次单点查询。离线下来扫描线,建立一棵线段树并使用 multiset 维护每个点处的标记;碰到上边界时范围插入取 \(\max\) 的标记,碰到下边界时范围删除取 \(\max\) 的标记,查询时答案即链上的每个点处各标记的最大值。时间复杂度 \(O(n \log^2 n+q \log n)\)

Code

#include <bits/stdc++.h>
#define eb emplace_back
#define fi first
#define se second
#define L (ls[rt])
#define R (rs[rt])
using namespace std;
const int N=2e5+5,M=4e5+5,Q=1e6+5;

namespace IO{
	inline char nc(){
		static char buf[500001],*p1=buf,*p2=buf;
		return p1==p2&&(p2=(p1=buf)+fread(buf,1,500000,stdin),p1==p2)?EOF:*p1++;
	}
	char out[500001],*pout=out,*eout=out+500000;
	template<typename T> inline T read(){
		char ch=nc(); T sum=0; bool f=false;
		for(;ch<'0'||ch>'9';ch=nc()) if(ch=='-') f=1;
		while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
		return f ? -sum : sum;
	}
}
#define read IO::read<int>

int n,q,m,len,tot,c[N],a[N],stk[N],res[Q]; vector<pair<int,int> >qry[N];
int b[N],frt[N],nxt[N],ls[M],rs[M]; vector<int> vec[N]; priority_queue<int> A[M],B[M];
struct Operation{int l,r,val;};vector<Operation> ins[N],ers[N];
void Contribute(){
	stk[len=0]=0;
	for (int i=2;i<=m;i++){
		while (len&&a[stk[len]]>=a[b[i]])  len--;
		frt[i]=stk[len],stk[++len]=b[i];
	}
	stk[len=0]=n+1;
	for (int i=m-1;i;i--){
		while (len&&a[stk[len]]>a[b[i]])  len--;
		nxt[i]=stk[len],stk[++len]=b[i];
	}
	for (int i=2;i<m;i++){
		int l=frt[i]+1,r=nxt[i]-1,p=b[i];
		ins[l].eb(Operation{p,r,a[p]});
		ers[p].eb(Operation{p,r,a[p]});
	}
}
int build_tree(int l,int r){
	int rt=(++tot);
	if (l^r){
		int mid=(l+r)>>1;
		ls[rt]=build_tree(l,mid);
		rs[rt]=build_tree(mid+1,r);
	}
	return rt;
}
void upd(int nl,int nr,int l,int r,int rt,int k,int op){
	if (nl<=l&&r<=nr){
		if (op)  A[rt].push(k);
		else B[rt].push(k);
		return;
	}
	int mid=(l+r)>>1;
	if (nl<=mid)  upd(nl,nr,l,mid,L,k,op);
	if (nr>mid)  upd(nl,nr,mid+1,r,R,k,op);
}
void query(int nl,int l,int r,int rt,int &res){
	if (!A[rt].empty()&&A[rt].top()>res){
		while (!A[rt].empty()&&!B[rt].empty()&&A[rt].top()==B[rt].top())  A[rt].pop(),B[rt].pop();
		if (!A[rt].empty())  res=max(res,A[rt].top());
	}
	if (l==r)  return;

	int mid=(l+r)>>1;
	if (nl<=mid)  query(nl,l,mid,L,res);
	else query(nl,mid+1,r,R,res);
}
int main(){
	n=read(),q=read();
	for (int i=1;i<=n;i++)  c[i]=read(),vec[c[i]].eb(i);
	for (int i=1;i<=n;i++)  a[i]=read();
	for (int i=1;i<=n;i++)if(!vec[i].empty()){
		b[m=1]=0;
		for (int x:vec[i])  b[++m]=x;
		b[++m]=n+1,Contribute();
	}
	build_tree(1,n);
	for (int i=1,l;i<=q;i++)  l=read(),qry[l].eb(make_pair(read(),i));
	for (int l=1;l<=n;l++){
		for (auto t:ins[l])  upd(t.l,t.r,1,n,1,t.val,1);
		for (auto t:qry[l])  query(t.fi,1,n,1,res[t.se]);
		for (auto t:ers[l])  upd(t.l,t.r,1,n,1,t.val,0);
	}
	for (int i=1;i<=q;i++)  printf("%d\n",res[i]);
	return 0;
}
posted @ 2023-02-24 21:52  ducati❤OI  阅读(123)  评论(0)    收藏  举报