我一开始先想到的是数组会滚动,所以只要记录取模x后的最早出现的数字就可以了
所以要维护一个最小出现次数dep,然后只要快速的找到最小出现的最早的数字就可以了
如何快速的维护呢(毕竟4e5不可能每次都O(n)找一遍)
我们设上一次的答案为before
所以想到如果这次出现的不是before,那么就还是before。
如果出现的是之前的数字,那么向后面找出现次数小于dep的数字,因为前面的数字一定大于等于before。
如果后面的数字都大于等于dep,那么我们在从头找起,找到第一个等于dep的位置
一次最大的更新一个是O(n),但每次均摊下来更新应该是O(1);
//不要问我是怎么算出来的
所以我写下了以下的代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 4e5+7;
const ll inf = 1e16+7;
typedef pair<int ,int >pii;
vector<pii>pi;
int prime[maxn];//素数数组
bool is_prime[maxn+ 10];//is_prime[i]表示i是素数
int numprime = 0;
void sieve(int n)
{
//素数个数计数器
for (int i = 0; i <= n; i++)
is_prime[i] = true;
is_prime[0] = is_prime[1] = false;//首先标记0和1不是素数
for (int i = 2; i <= n; i++)
{
if (is_prime[i]) //如果i是素数
{
prime[++numprime] = i;//将素数放进素数表
for (int j = 2 * i; j <= n; j += i)//所有i的倍数都不是素数
is_prime[j] = false;
}
}
}
int vis[maxn];
int b[maxn];
int T,n,x,cc;
int dep = 0;
int main()
{
scanf("%d%d",&n,&x);
int be =x+ 1;
for(int i=1;i<=n;i++)
{
scanf("%d",&cc);
if(x==1)
{
cout<<i<<endl;
continue ;
}
cc%=x ;
vis[b[cc]] ++;
b[cc]++ ;
if(vis[dep] == x)dep ++ ;
if(be == x+1)
{
if(b[0]==1)
be = 1;
else be =0 ;
printf("%d\n",be);
continue;
}
if(cc != be)
{
printf("%d\n",be+dep*x);
continue;
}
else {
int flag = 1 ;
for(int i=be+1;i<x;i++)
{
if(b[i] == dep-1)
{
flag = 0 ;
be = i ;
printf("%d\n",be+dep*x);
break;
}
}
if(flag == 1 )
{
for(int i=0;i<x;i++)
{
if(b[i] ==dep )
{
be =i;
printf("%d\n",be+dep*x);
break;
}
}
}
}
}
return 0 ;
}
然后他T了
我百思不得其姐
所以我去看了看龍木巨巨的答案
我震惊了不愧是龍木聚聚
#include<bits/stdc++.h>
using namespace std;
long long n,t,k,b,c,s,x,q,y;
map<long long,long long>m;
long long a[10000001];
int main()
{
scanf("%lld %lld",&q,&x);
t=0;
while(q--)
{
scanf("%lld",&y);
m[y%x]++;
while(m[t%x]>=1)
{
m[t%x]--;
t++;
}
printf("%lld\n",t);
}
}
想法其实差不多,但是因为查询的值一定是递增的,所以我们可以直接对答案进行查询
膜拜!!!