题意大概:
给一个不超过 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写反导致数组越界,题中求合法情况的最大结果,我给求成了合法情况中的最大过程量。