题解: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;
}
posted @ 2025-10-21 09:27  TBSF_0207  阅读(10)  评论(0)    收藏  举报