题解:CF1852A Ntarsis' Set
提供了变式。
与 \(k\) 无关的 \(\Theta(n)\) 做法。
首先 \(10^{1000}\) 足够大,不妨把初始集合视为 \(\N_+\)。
模拟赛出了这个的强化版,\(k\le 10^9\),我们考虑怎么解决这个问题。首先观察到如果 \(a_1\neq 1\) 答案一定为 \(1\)。不妨逆推,我们发现顺着推很简单,如果经过若干轮都没有被淘汰,每一轮每个数 \(x\) 的 \(pos_x\) 就会经过如下转移:\(pos_x\leftarrow pos_x -i\),其中 \(a_i\) 为最后一个 \(<pos_x\) 的数。
相应地,逆推只需要 \(pos_x\leftarrow pos_x+i\),其中 \(a_i\) 为最后一个 \(<pos_x +i\) 的数,即 \(a_{i+1}\geq pos_x + i\)。显然递推过程中找到的 \(i\) 是单调不降的(理解:相当于每次抽出没被删掉的放在前面,只需找到每次被放在前面的推导出抽出时的位置,显然这是唯一且递增的),但是可能会有多个相同且冗余的 \(i\),每次都对 \(pos_x\) 执行 \(+i\) 的操作。
考虑加速这一过程。让 \(pos_x\) 直接加到边界 \(a_{i+1}-1\) 且不能做超过 \(k\) 步所需的步数为 \(p=\min(k,\lfloor\frac{a_{i+1}-1-pos_x}{i}\rfloor)\),那么 \(pos_x\leftarrow pos_x+p\times i\) 即可,这样对于每个 \(i\) 都只会做 \(\Theta(1)\) 次。最后如果 \(pos_x>a_n\) 且 \(k\neq 0\),\(pos_x\leftarrow pos_x + k\times n\) 就可以逆推出初始的 \(pos_x\) 值。
时间复杂度 \(\Theta(n)\)。
点击查看代码
```cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+5;
int n,k,a[N];
int main(){
int Tn;scanf("%d",&Tn);
while(Tn--){
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
if(a[1]!=1){printf("1\n");continue;}
LL Pos=1;
for(int i=1;i<n;i++)
if(Pos<=a[i+1]-i){
int p=min(k,(int)(a[i+1]-1-Pos)/i);
Pos+=p*i;k-=p;
}
Pos+=1ll*k*n;
printf("%lld\n",Pos);
}
return 0;
}
变式:如果每次删的是 \(n\) 个不连续区间 \([L_i,R_i]\),如何快速处理?
我们找出这些区间的间隙 \([L'_i,R'_i]\),那么我们顺推所做的就是每次把所有 \([L'_i,R'_i]\) 拼起来然后推到前面,最后一个区间是 \([L'_n,+\infty)\)。因此逆推时找到其对应位置即可,每次所处位置 \(pos_x\) 一定处于某个间隙区间。用一个指针推当前所处间隙区间即可,如果多次转移处于一个间隙区间用上述方法加速,否则直接推指针找到下一个所处间隙区间。这样做也是 \(\Theta(n)\) 的。
这是考场写的针对原题的区间合并代码。
点击查看代码
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2e5+5;
int n,k,a[N],L[N],R[N],cnt;
int nL[N],nR[N],pre[N],gp[N];
LL solve(int Pos,int u,int k){
if(!k)return Pos;
if(u==cnt)return Pos+1ll*k*pre[cnt];
if(Pos<=nR[u]-pre[u]){
int dt=pre[u];
int res=(nR[u]-Pos)/pre[u],rd=min(res,k);
Pos+=rd*dt;k-=rd;
if(!k)return Pos;
}
int pos=u+1;
while(pos<cnt&&nR[pos]-pre[pos]<Pos)pos++;
return solve(Pos+pre[pos],pos,k-1);
}
int main(){
freopen("superset.in","r",stdin);
freopen("superset.out","w",stdout);
int ID,Tn;scanf("%d%d",&ID,&Tn);
while(Tn--){
scanf("%d%d",&n,&k);
a[0]=-1;cnt=0;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
if(a[i]!=a[i-1]+1)L[++cnt]=a[i];
R[cnt]=a[i];
}
if(a[1]!=1){printf("1\n");continue;}
for(int i=1;i<cnt;i++)
nL[i]=R[i]+1,nR[i]=L[i+1]-1,
pre[i]=pre[i-1]+R[i]-L[i]+1;
nL[cnt]=R[cnt]+1,nR[cnt]=-1;
pre[cnt]=pre[cnt-1]+R[cnt]-L[cnt]+1;
printf("%lld\n",solve(nL[1],1,k-1));
}
return 0;
}

浙公网安备 33010602011771号