AtCoder Beginner Contest 194 E - Mex Min

题意大概:

给一个不超过 1.5 × 1 0 6 1.5\times10^6 1.5×106的数组 A 1 A_1 A1 A i A_i Ai长度为 m m m,和一个 n n n,给出每连着的 n n n个数中没出现的最小数中的最小数。

思路:

一看这题不就滑动窗口(在这里有详lue解)么!!!在其他oj上粘过来之后改了改就直接交了,结果就
在这里插入图片描述
又看了看题后注意到只要是没出现的数中的最小数就行。数据也不大,开个数组记录前 n n n个数中每个数的出现次数并找出没出现的数中的最小数,然后双指针一个指头一个指尾,每次把 A 尾 A_尾 A的出现次数减一, A 头 A_头 A的出现次数加一,判断 A 尾 A_尾 A出现的次数是否为0,如果为0就与之前记录的最小值比较,当前没出现过的最小值若比之前没出现过的最小值小,则当前没出现过的最小值之前一定是在队中。
这么说吧,以我手打的一个比较直观的样例来说

Input

7 3
2 0 1 2 0 1 0

Output

2
在这里插入图片描述
所以输出答案是2,这样够直观了吧,而且因为 A i A_i Ai不超过 1.5 × 1 0 6 1.5\times10^6 1.5×106,而开这么大的数组绰绰有余。

代码

#include <bits/stdc++.h>
using namespace std;
const int N=2e6;
int num[N],a[N],n,k,ans=9999999;
bool flag;
void work()
{
    
    
	int head=k,tail=1;
	for(int i=tail;i<=head;i++)
	{
    
    
		num[a[i]]++;
	}
	for(int i=0;;i++)
	{
    
    	
		if(num[i]==0)
		{
    
    
			ans=min(ans,i);
			if(ans==0)
			{
    
    
				cout<<0;
				flag=1;
				return;
			}
			break;
		}
	}	
	while(head<n)
	{
    
    
		num[a[tail]]--;
		tail++;
		num[a[head]]++;
		if(num[a[tail]]==0)
		{
    
    
			ans=min(ans,a[tail]);
		}
		head++;
	}
}
int main()
{
    
    
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	work();
	if(flag==0)
		cout<<ans;
	return 0;
}

然后WA了5组死活改不出来。打完比赛给高二学长发过去看了看,得知高二三位学长分别是用树状数组,主席树和线段树水过去的。这里有树状数组的代码:
我看不懂,所以不解释

#include<bits/stdc++.h>
#define N 1600006
#define LL long long 
#define LB long double
using namespace std;

int n,m;
int ans=1e8,a[N],sp=0;
int mp[N],tr[N];

inline int qr()
{
    
    
	char a=0;int w=1,x=0;
	while(a<'0'||a>'9'){
    
    if(a=='-')w=-1;a=getchar();}
	while(a<='9'&&a>='0'){
    
    x=(x<<3)+(x<<1)+(a^48);a=getchar();}
	return x*w;
}

const int K=1500002;

inline void add(int x,int k)
{
    
    
	for(register int i=x;i<=K;i+=i&-i) tr[i]+=k;
}

inline int que(int x)
{
    
    
	int res=0;
	for(register int i=x;i;i-=i&-i) res+=tr[i];
	return res;
}

int jac[123];
inline int ask()
{
    
    
	int pos=1;
	for(register int i=21;i>=0;i--)
		if(pos+jac[i]-1<=K)
		{
    
    
			int op=que(pos+jac[i]-1);
			if(op==pos+jac[i]-1)
				pos+=jac[i];
		}
	return pos;
}

int main()
{
    
    
	n=qr();
	m=qr();
	jac[0]=1;
	for(register int i=1;i<=21;i++) jac[i]=jac[i-1]<<1;
	for(register int i=1;i<=n;i++) a[i]=qr()+1;
	for(register int i=1;i<=n;i++)
	{
    
    
		mp[a[i]]++;
		if(mp[a[i]]==1) add(a[i],1);
		if(i>=m)
		{
    
    
			ans=min(ans,ask());
			mp[a[i-m+1]]--;
			if(!mp[a[i-m+1]]) add(a[i-m+1],-1);
		}
	}
	printf("%d\n",ans-1);
	return 0;
}

就是这位神仙写的

然后我的代码改完后也被精简了一下

#include <bits/stdc++.h>
using namespace std;
const int N=2e6;
int num[N],a[N],n,k,ans=9999999;
bool flag;
void work()
{
    
    
	int tou=1,wei=k;
	for(int i=1;i<=k;i++)num[a[i]]++;
	for(int i=0;;i++)
	if(num[i]==0){
    
    ans=i;break;}
	if(ans==0)return;
	while(wei<n){
    
    
		num[a[++wei]]++;
		num[a[tou]]--;
		if(num[a[tou]]==0)
		ans=min(ans,a[tou]);
		tou++;
	}
}
int main()
{
    
    
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);
	work();
	cout<<ans;
	return 0;
}

其实主要就是我的尾端点移动的早了,应该先判断是否出 a 尾 a_尾 a是否出现次数是否为 0 0 0再让尾移动。
好了就这样,这是我做过AtCOder的ABC中最简单的E题,但是考试时候的失误让我错失500分。淦,这又让我想起之前之前模拟赛的T1,本来准备写记忆化,但是x和y写反导致数组越界,题中求合法情况的最大结果,我给求成了合法情况中的最大过程量。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/ydsrwex/article/details/114528520