codeforces1436 E. Complicated Computations

https://codeforces.com/contest/1436/problem/E

直接用权值树状数组暴力模拟就好了,想了一年想不出来

树状数组b[i]记录值为i上次出现的位置在哪,getmi求前缀最小位置在哪里

然后枚举右端点,不断地更新最小位置,一开始b[i]初值全部设成n+1

然后考虑直接右端点就是n,每个数字上次出现的位置就是他最后出现的位置

如果对于某个数字i,last[i]!=n && getmi(i-1)>last[i],也就是从末尾到i最后出现的位置,1-i-1每个数字都出现过,但是i没有出现过,那么这一段的mex值就是i,那么vis[i]=true;

接着从后往前更新,每次把i这个数字去掉,也就是右端点从i-1开始,那么就把b[a[i]]更新为pre[i]就行了,然后考虑要不要vis[a[i]]=true

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

const int maxl=3e5+10;

int n,m,cnt,tot,cas,ans;
int a[maxl],b[maxl],last[maxl],pre[maxl],nxt[maxl]; 
bool vis[maxl];
char s[maxl];

inline void prework()
{
	scanf("%d",&n);
	ans=1;
	for(int i=1;i<=n+1;i++)
		last[i]=0,pre[i]=n+2;
	for(int i=1;i<=n;i++)
	{	
		scanf("%d",&a[i]);
		pre[i]=last[a[i]];
		last[a[i]]=i;
	}
}

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

inline int getmi(int i)
{
	int ret=n+2;
	while(i)
	{
		ret=min(ret,b[i]);
		i-=i&-i;
	}
	return ret;
}

inline void mainwork()
{
	for(int i=1;i<=n;i++)
		b[i]=n+1;
	for(int i=1;i<=n;i++)
		upd(i,last[i]);
	for(int i=1;i<=n+1;i++)
	if(last[i]!=n && getmi(i-1)>last[i])
		vis[i]=true;
	for(int i=n;i>=1;i--)
	{
		upd(a[i],pre[i]);
		if(pre[i]!=i-1 && getmi(a[i]-1)>pre[i])
			vis[a[i]]=true;
	}
	ans=1;
	while(vis[ans])
		ans++;
}

inline void print()
{
	printf("%d\n",ans);
}

int main()
{
	int t=1;
	//scanf("%d",&t);
	for(cas=1;cas<=t;cas++)
	{
		prework();
		mainwork();
		print();
	}
	return 0;
}

猜你喜欢

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