题目大意:
首先,给你一个数列1e6,并给定你一个数字A,然后让你选出一个数字B,满足,对于数列中任意一个位置,该位置之前出现的A的个数不大于B出现的次数。
分析:
首先,我用一个跳跃数组a[i]来表示值i是否可能是答案。这里我说的跳跃数组就是,对于数组的某一个位置,我可以直接跳到它的下一个位置或者上一个位置,这样我就可以在删除其中的若干元素之后在下一次遍历的时候,不遍历这些被删除的元素。然后我就按照所给数列出现的顺序一个一个的添加到计数数组num中去,遇到数字A,我就遍历一遍跳跃数组并删除所需删除的元素。
下面分析一下这个办法的时间复杂度:
首先,出现一个数字A,就会遍历一次跳跃数组,那么,A第i次出现的时候,跳跃数组需要遍历的位置最多只需要n/i次。那么总的遍历次数就是
后续:
然后看了下别人的代码,好像我就是想麻烦了。只要用p和sum分别记录当前可以选择的最大值和最大值出现的次数,然后随着输入新的数,不断地更新这个信息就好了。同时记录每个数出现的次数。这样当一个数添加的时候,如果他之前出现的次数小于之前A出现的次数,那么我就不记录他的这次出现了,那么他就相当于永远不能超过A出现的次数了。这样一次遍历O(n)的时间就完成了解答。
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 1000500
#define maxe 1000000
struct node
{
bool can;//是否可能为答案
int next;//下一个可能为答案的位置标号
int last;//上一个可能为答案的位置标号
}a[maxn];
int n,A,temp,first,appear_num[maxn],A_num;
void init()
{
first=1;A_num=0;
for(int i=0;i<maxn;i++)
{
a[i].can=1;
a[i].next=i+1;
a[i].last=i-1;
}
}
void del(int x)
{
a[x].can=0;
if(a[x].last!=-1)a[a[x].last].next=a[x].next;
if(a[x].next<=maxe)a[a[x].next].last=a[x].last;
if(x==first)
{
first=a[x].next;
}
}
int main()
{
scanf("%d%d",&n,&A);
init();
del(A);
for(int i=0;i<n;i++)
{
scanf("%d",&temp);
if(temp!=A)
{
if(a[temp].can==0)continue;
appear_num[temp]++;
}
else
{
if(first>maxe)continue;
int t=first;
A_num++;
while(t<=maxe)
{
if(appear_num[t]<A_num)
{
del(t);
}
t=a[t].next;
}
}
}
//cout<<first;
printf("%d",(first>maxe)?-1:first);
}