CF1060G Balls and Pockets
令 \(f(i)\) 表示 \(i\) 的后继。
首先可以发现,在 \([0,a_1)\) 中的数的状态不会改变,所以对于 \(x<a_1\) 的询问,直接输出 \(x\) 即可。
而当 \(x\ge a_n\) 时,存在 \(f(x)=x+n\)。因此当 \(x\) 较大时,询问可以直接计算。又因为 \(f(i)>i\),所以在经过 \(\mathcal O(a_n)\) 次操作后 \(x\) 将会大于 \(a_n\),所以在 \(k\) 很大时计算答案就可以方便许多。
然后假设第一次操作之后位置 \(i\) 上的球的编号变成了\(f(i)\),可以发现之后每一次操作,如果原来的数编号为 \(i\),操作之后编号也会变成 \(f(i)\)。
证明考虑归纳:
第一次操作后,\(i\) 变成了 \(f(i)\),\(f(i)\) 位置上的数变成了 \(f(f(i))\)。第二次操作时 \(i\) 位置上的数变成了 \(f(f(i))\)。大概就是每一次操作都是让 \(f(i)\) 取代 \(i\) 位置上的数,因此第二次操作 \(i\) 位置上的数就变成了 \(f(f(i))\)。\(k\ge 2\) 时同理。
接下来为了方便,假设 \(a_1=0\)。
令 \(g(x)\) 为一个集合 \(g(x)=\{x,f(x),f(f(x)),\cdots \}\),可以发现 \(g(a_i)\) 就是第 \(i\) 个洞吞掉的所有数。
显然 \(g(a_1)\cup g(a_2)\cup \cdots \cup g(a_n)=\mathbb{N}\)。因此 \(\mathbb N\) 就这样被划分成了 \(n\) 个集合。而其他的 \(g(x)(x\neq a_i)\) 可以发现一定是某个 \(a_i\) 的子集。
而 \(g(a_1)\) 由若干段等差数列构成,公差分别为 \(1\to n\)。
将公差为 \(d\) 的等差数列的最后一个元素称为 \(P_d\),特别的,若公差为 \(d\) 的集合为空,那么 \(P_d=P_{d-1}\)。可以发现 \(P_d<a_{d+1}<f(P_d)\)。
考虑 \(P_d\) 与 \(f(P_d)\) 中的 \(d\) 个元素(可以证明恰好为 \(d\) 个),考虑他们所属的集合 \(g(x)\)。
将 \(d\) 从 \(n-1\) 到 \(1\) 考虑,可以得到以下性质:
对每个 \(0\le d<n\),可以构造一个大小为 \(d\) 的集合 \(S_d\)。
- 
对于 \(d=n-1\),有 \(S_d=\{1,2,\cdots ,n-1\}\)。 
- 
对于 \(P_d<a_{d+1}< f(P_d)\),令 \(x=a_{d+1}-P_d(1\le x\le d)\),则令 \(S_{d-1}=S_d\backslash \{y\}\),其中 \(y\) 是 \(S_d\) 中第 \(x\) 小的元素。 
这样构造出来的 \(S_d\) 满足:对于一对 \((i,f(i)),i\in g(a_1)\),设 \(P_d\le i<P_{d+1}\),则 \(i\) 与 \(f(i)\) 之间有 \(d\) 个元素,设 \(S_d=\{v_1,v_2,\cdots ,v_d\}\),则 \(i+j\) 所属的集合 \(g(x)\) 就是 \(P_{n-1}+v_j\) 所属的集合。
因此,需要先求出 \(f^{(k)}(0)\)。
具体地,通过二分求出 \(g(0)\) 第 \(k\) 项所处等差数列的公差,然后可以得到属于该等差数列的第几项,最后通过 \(P_{d-1}+m\cdot d\) 等式子计算。
若 \(x\in g(0)\),那么只需要再二分求出 \(x_i\) 是 \(g(0)\) 的第 \(y\) 项,答案就是 \(g(0)\) 的第 \(x+y\) 项。
否则,只需要找到 \(i\) 满足 \(i\in g(0)\) 并且 \(i<x<f(i)\),就可以得到 \(x\) 所在的集合。又因为 \(f^{(k)}(i)<f^{(k)}(x)<f^{(k+1)}(i)\),因此只需要知道在 \(f^{(k)}(i)\) 和 \(f^{(k)}(i)\) 之间,与 \(x\) 同集合的数是它们之间的第几个,这个可以通过逆用上面那个性质解决。
然后考虑一下如果计算和储存 \(S_d\),由于相邻的两个集合间只相差一个数,因此可以用可持久化线段树维护。
而第 \(x\) 小显然可以直接树状数组。
计算并维护 \(S_d\) 时间复杂度为 \(\mathcal O(n\log n)\),对于单次询问,找到它在第几项需要一个二分,里面计算还要一个二分,时间复杂度 \(\mathcal O(\log x_i\log n)\)。最后计算答案,需要计算集合指定项和线段树二分,时间复杂度 \(\mathcal O(log n)\) 。
因此总时间复杂度为 \(\mathcal O(n\log n+m\log x_i\log n)\)。
#include<bits/stdc++.h>
#define int long long
#define R(i,a,b) for(int i=(a),i##E=(b);i<=i##E;i++)
#define L(i,a,b) for(int i=(b),i##E=(a);i>=i##E;i--)
using namespace std;
int n,q;
int a[111111];
int rnd[111111],sum[111111],en[111111];
int mod[111111];
struct bit
{
	int v[111111];
	inline void modify(int x,int k)
	{
		for(;x<=n;x+=x&(-x)) v[x]+=k;
	}
	inline int query_sum(int x)
	{
		int ret=0;
		for(;x;x-=x&(-x))ret+=v[x];
			return ret;
	}
	inline int query_kth(int x)
	{
		int l=1,r=n,best=n+1;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			if(query_sum(mid)>=x) best=mid,r=mid-1;
			else l=mid+1;
		}
		modify(best,-1);
		return best-1;
	}
}bit;
struct clsgt
{
	int tim;
	int siz[3333333],ls[3333333],rs[3333333],rt[111111];
	inline void modify(int p,int l,int r,int lst,int &x)
	{
		x=++tim;
		siz[x]=siz[lst]+1;ls[x]=ls[lst],rs[x]=rs[lst];
		if(l==r) return;
		int mid=(l+r)>>1;
		if(p<=mid) modify(p,l,mid,ls[lst],ls[x]);
		else modify(p,mid+1,r,rs[lst],rs[x]);
	}
	inline int find_kth(int k,int l,int r,int x)
	{
		if(l==r) return l;
		int mid=(l+r)>>1;
		if(k<siz[ls[x]]) return find_kth(k,l,mid,ls[x]);
		else return find_kth(k-siz[ls[x]],mid+1,r,rs[x]);
	}
	inline int query_sum(int p,int l,int r,int x)
	{
		if(r==p||!x) return siz[x];
		int mid=(l+r)>>1;
		if(p<=mid) return query_sum(p,l,mid,ls[x]);
		else return query_sum(p,mid+1,r,rs[x])+siz[ls[x]];
	}
}st;
pair<int,int> check(int p,int x,int k)
{
	k+=rnd[p]-x;
	int t=upper_bound(rnd,rnd+n,k)-rnd;
	return make_pair(1ll*k*t-sum[t],t);
}
int find1(int x,int k)
{
	int l=1,r=n-1,best=0;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(mid,k,0).first<=x) best=mid,l=mid+1;
		else r=mid-1;
	}
	return best;
}
int find2(int p,int x,int k)
{
	int l=1,r=rnd[p+1]-rnd[p],best=0;
	if(p==n-1) r=x/n+k+1;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(check(p,k,mid).first<=x) best=mid,l=mid+1;
		else r=mid-1;
	}
	return best;
}
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(NULL);	
	int a0;
	cin>>n>>q>>a[0];a0=a[0];
	R(i,1,n-1)
	{
		cin>>a[i];
		a[i]-=a[0];
	}
	a[0]=0;
	R(i,1,n-1) 
	{
		int x=(a[i]-en[i-1])/i;
		rnd[i]=rnd[i-1]+x,en[i]=en[i-1]+x*i,sum[i+1]=sum[i]+rnd[i];
	}
	R(i,1,n) bit.modify(i,1);
	L(i,0,n-1) mod[i]=bit.query_kth(a[i]-en[i]+1);
	R(i,0,n-1) st.modify(mod[i],0,n-1,st.rt[i],st.rt[i+1]);
	while(q--)
	{
		int x,k;
		cin>>x>>k;
		if(x<a0)
		{
			cout<<x<<'\n';
			continue;
		}
		x-=a0;
		int fi=find1(x,k),se=find2(fi,x,k);
		auto ret=check(fi,k,se);
		int rk=st.find_kth(x-ret.first,0,n-1,st.rt[ret.second]);
		int ans=en[fi]+se*(fi+1)+a0+((rk>0)?st.query_sum(rk-1,0,n-1,st.rt[fi+1]):0);
		cout<<ans<<'\n';
	}
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号