E Phone Network 2020ICPC·小米 网络选拔赛第一场

https://ac.nowcoder.com/acm/contest/7501/E

看了看题解没太看懂怎么维护最小值的,学习了一下杭二oi爷的代码学会了

按数字1-m的顺序求答案,因为求数字i的时候必须1-i-1都要出现过,所以求当前数字直接在之前的基础之上维护就行了

我们维护从每个位置开始,1-i都出现过的最小右端点位置Ri,l,跟题解里面定义是一样的,按从左到右顺序枚举数字的位置,我们就是需要吧[last+1,id]这一段中小于id的更新为id,用树状数组维护前缀最大值,每个Ri,l等于premx(l),就只要从last+1这个位置更新就行了,因为Ri,l是严格非下降的,所以只要upd(last+1,id),就能把[last+1,id]之一段区间给更新至少到id

然后i这个数字的答案就是当前某个最小的premx(i)-i+1

我们发现每次段更新,这个答案发生变化只会在l-1(也就是这个数字上次出现的地方),如果从last+1开始用id更新成功了,变化还可能发生在当前这段区间premx()=id的最右端点pos

那么我们只要把这些位置都存进一个priority_queue中,每次取出较小的,如果当前更新过去导致那个位置的premx变了,就弹出优先队列,这样就算当前这个i没有更新出一个比ans[i-1]更大的值,我们还是找到当前答案

#include<bits/stdc++.h>
using namespace std;

const int maxl=2e5+10;

int n,m,mi;
int a[maxl],b[maxl],ans[maxl];
queue<int> c[maxl];
typedef pair<int,int> p;
priority_queue<p,vector<p>,greater<p> >q;

inline void prework()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		c[a[i]].push(i);
	}
}

inline void upd(int i,int x)
{
	while(i<=n)
	{
		b[i]=max(b[i],x);
		i+=i&-i;
	}
}

inline int premx(int i)
{
	int ret=0;
	while(i)
	{
		ret=max(b[i],ret);
		i-=i&-i;
	}
	return ret;
}

inline void ins(int i)
{
	if(i<=0 || i>n) return;
	q.push({premx(i)-i+1,i});
}

inline void solv(int l,int r,int id)
{
	r=min(r,n);
	if(l>r) return;
	upd(l,id);
	ins(l-1);
	if(premx(l)!=id) 
		return;
	int mid,pos=l;
	while(l<=r)
	{
		mid=(l+r)>>1;
		if(premx(mid)==id)
			pos=mid,l=mid+1;
		else
			r=mid-1;
	}
	ins(pos);
}

inline void mainwork()
{
	for(int i=1;i<=m;i++)
	{
		c[i].push(3*n);mi=n;
		int last=0,id;
		while(!c[i].empty())
		{
			id=c[i].front();c[i].pop();
			solv(last+1,id,id);
			last=id;
		}
		id=q.top().second;
		while(q.top().first!=premx(id)-id+1)
			q.pop(),id=q.top().second;
		ans[i]=q.top().first;
	}
}

inline void print()
{
	for(int i=1;i<=m;i++)
		printf("%d ",ans[i]);
}

int main()
{
	prework();
	mainwork();
	print();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/liufengwei1/article/details/109319332