CF1852A Ntarsis' Set
题意
给定一个包含 $1$ 到 $10^{1000}$ 的集合 $S$。每次同时删除 $S$ 中的第 $a_1$ 个、第 $a_2$ 个、$\ldots$ 个、$a_n$ 个最小的数。
询问经过 $ k $ 天后,集合 $S$ 中最小的元素。
Solution
发现答案具有单调性,考虑二分答案。对于一个 $mid$,发现删除操作事实上是降低剩余数字的排名,故不断减去小于等于 $mid$ 的 $a_i$ 个数,即 $mid$ 在 $k$ 次删除中每次降低的排名。最后若排名大于 $0$,则当前 $mid$ 合法,寻找最小的合法的 $mid$ 即可。
注意特判 $a_1 \ne 1$ 的情况,此时答案一定为 $1$。
时间复杂度为 $\mathcal{O}(k\log nk)$,可以通过本题。
code
#include<bits/stdc++.h>
using namespace std;
inline long long read()
{
long long res=0,flag=1;
char ch=getchar();
while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
return res*flag;
}
long long n,k;
long long val[200010];
bool check(long long mid)
{
int point=n;
for(int i=1;i<=k;i++)
{
while(val[point]>mid)
point--;
mid-=point;
}
return mid>0;
}
int main(int argc,const char *argv[])
{
int T=read();
while(T--)
{
n=read(),k=read();
for(int i=1;i<=n;i++)
val[i]=read();
long long left=1,right=n*k+n,ans=n*k+n;
if(val[1]!=1)
{
printf("1\n");
continue;
}
while(left<right)
{
long long mid=(left+right+1)>>1;
if(check(mid)==true)
ans=mid,right=mid-1;
else
left=mid;
}
printf("%lld\n",ans);
}
return 0;
}
Solution
其实这道题有时间复杂度为 $\mathcal{O}(n+k)$ 的做法。
假设序列 $a$ 递增。考虑反向模拟,我们每次不删除操作中位置 $a_1,a_2,\cdots,a_n$ 的数字,然后再 check 第 $k$ 次操作后的第一个数字。从数字 $1$ 开始,再每次运算中,尽量在 $a_1-1, a_2-2, \cdots, a_{n}-n$ 后插入 $0$,使 $0$ 在插入结束后占据位置 $a_1, a_2, \cdots, a_n$。在插入 $k$ 次后,$1$ 所在的位置即为答案。
定义 $1$ 的位置为 $now$,在具体实现中,我们记 $cnt$ 为 $now$ 之前有多少个 $a_1-1,a_{2}-2,\cdots,a_{n}-n$,则每个插入可以以 $\mathcal{O}(1)$ 的时间复杂度处理。即如果 $a_1-1$ 到 $a_i-i$ 都在 $x$之前,那么在 $now$ 之前插入 $i$ 个零。
code
#include<bits/stdc++.h>
using namespace std;
inline long long read()
{
long long res=0,flag=1;
char ch=getchar();
while(!isalnum(ch)) (ch=='-')?flag=-1:1,ch=getchar();
while(isalnum(ch)) res=res*10+ch-'0',ch=getchar();
return res*flag;
}
long long val[200010];
int main(int argc,const char *argv[])
{
int T=read();
while(T--)
{
long long n=read(),k=read();
for(int i=1;i<=n;i++)
val[i]=read();
if(val[1]!=1)
{
printf("1\n");
continue;
}
long long cnt=1,now=1;
for(int i=1;i<=k;i++)
{
while(cnt<=n&&val[cnt]<=now+cnt-1)
cnt++;
now+=cnt-1;
}
printf("%lld\n",now);
}
return 0;
}

浙公网安备 33010602011771号