ABC346G

题意:

给定一个 \(N\),求所有 \(N\) 的子区间 \([l,r],1\leq l\leq r\leq N\) 中满足 \(i \in [l,r]\) 中有至少一个 \(A_i\) 的出现次数有且仅出现一次。

题意很明确,如何解决?

暴力:

直接 \(N^2\) 扫一遍然后进行每个区间的特判即可,复杂度 \(O(N^3)\) 估计只能过样例。

莫队:

由于发现对于一个区间 \([l,r]\) 只要其中一个 \(i \in [l,r]\) 中的 \(count(A_i)\) 仅仅出现一次且拓展的 \([l,r]\) 如果不等于 \(A_i\) 那么这个拓展的区间也是合法的区间。故可以用莫队做,但是可能会很麻烦。具体的:

inline void upd(int x){
  mp[a[x]]--;
}
inline bool que(int x){
  if(mp[a[x]]>=1){
    return false;
  }
  return true;
}

这样做是 \(O(N^2\sqrt N)\) 的反正也过不了。

正解:

考虑用 \(L_i\) 表示 \(i\) 最左出现的位置, \(R_i\) 表示 \(i\) 最右边出现的位置,那么对于一个 \(i\) 肯定满足 \([L_{A_i},i]\) 满足和 \([i,R_{A_i}]\) 满足,并且 \([L_{A_i}-1,.....k<R]\) 都满足,同理右端点也一样,所以由乘法原理得所有满足的区间个数有 \((R_i-i+1)(i-L_i+1)\) 个。
但是这样做会发现有很多数会重复计算,所有可以用扫描线算法每个 \(i\) 对应了左上角为 \((L_i,R_i)\) 右下角为 \((i,i)\) 的矩形来计算重合的部分,(只不过是一维的)也可以用二维线段树的区间赋值,于是答案即为重合部分的面积,复杂度为 \(O(N\log_2 N)\) 可以过的。

具体的:

\(pos_{i,j,k}\) 表示 \(i\)\(j\) 后面(也就是右面) 所出现的第 \(k\) 次的位置。
那么有形如双指针的方法枚举左端点 \(l\) 考虑 \(r\) 的取值,即-右端点 \(r\) 应该满足的条件,显然,当有一个数 \(x\) 时,左端点为 \(l\) 则右端点为 \([pos_{x,l,1},pos_{x,l,2})\) 这里 \(pos_{x,l,2}\) 不能取,因为如果取就会出现两个 \(x\)
我们要在每次 \(l\) 往右都有且仅有一个值给 \(l\) 这个值就是 \(A_l\) 那么会给 \(l\) 所贡献的区间会从 \([l,pos_{l,a_l,2})\) 同理,往后的区间会是 \([pos_{l,a_l,2},pos_{l,a_l,3})\) 其实就是把这个区间向后移了一位
然后发现这个并集大小的维护实际上是要我们支持区间加 / 减查询区间非 0 数个数,同时保证任意时刻所有数非负。于是直接把扫描线维护矩形面积并的线段树拿过来即可。具体地,维护区间内最小值及其个数,查询时若区间最小值为 0,则返回区间长度减去最小值个数,否则返回区间长度。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,a[N],l[N],r[N];
int mp[N];
long long ans=0;

struct edge{
	int ll,rr,hh,ff;
}e[N<<1];
int cnt;
inline int ls(int p){
	return p<<1;
}
inline int rs(int p){
	return p<<1|1;
}
void add(int l,int r,int h,int f){
	e[++cnt].ll=l;
	e[cnt].rr=r;
	e[cnt].hh=h;
	e[cnt].ff=f;
}
bool operator <(const edge &a,const edge &b){
	if(a.hh==b.hh)return a.ff>b.ff;
	return a.hh<b.hh; 
}
struct node{
	int sum,len;
}tr[N<<2];
void pushup(int p,int l,int r){
	if(tr[p].sum) tr[p].len=r-l+1;
	else if(l==r) tr[p].len=0;
	else tr[p].len=tr[ls(p)].len+tr[rs(p)].len;
}
void update(int p,int l,int r,int L,int R,int val){
	if(L<=l&&r<=R){
		tr[p].sum+=val;
		pushup(p,l,r);
		return ;
	}
	int mid=(l+r)>>1;
	if(L<=mid)update(ls(p),l,mid,L,R,val);
	if(R>mid) update(rs(p),mid+1,r,L,R,val);
	pushup(p,l,r);
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(nullptr);cout.tie(nullptr); 
	cin>>n;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int i=1;i<=n;i++){
		l[i]=mp[a[i]]+1;
		mp[a[i]]=i;
	}
	for(int i=1;i<N;i++) mp[i]=0;
	for(int i=n;i>=1;i--){
		if(mp[a[i]])r[i]=mp[a[i]]-1;
		else r[i]=n;
		mp[a[i]]=i;
	}
	for(int i=1;i<=n;i++){
		add(i,r[i]+1,l[i],1);
		add(i,r[i]+1,i+1,-1);
	}
	sort(e+1,e+1+cnt);
	for(int i=1;i<cnt;i++){
		update(1,1,n+1,e[i].ll,e[i].rr-1,e[i].ff);
		ans+=1ll*tr[1].len*(e[i+1].hh-e[i].hh);
	}
	cout<<ans;
	return 0;
}
posted @ 2024-03-28 21:42  tomxi  阅读(8)  评论(0编辑  收藏  举报