CF1060G Balls and Pockets 题解

· · 题解

$\rm Difficulty:3400

题意

有一个无限长的序列 p=\{0,1,2,3,4,\dots\},其中第 i 个位置上的数是 i

给定长度为 n 的非负整数序列 a,我们对序列 p 做无限次以下操作:

### 做法 设 $f(x)$ 表示第一次操作后 $p_x$ 的值,那么可以得到每次操作后 $p_x$ 会变成 $p_{f(x)}$,以及第 $k$ 次操作后 $p_x=f^k(x)$。 第二个结论可以归纳证明:第一次操作后 $p_x=f(x),p_{f(x)}=f(f(x)),\dots$,以此类推可以证明上述结论。 上面考虑了对于一个位置上的值的性质,接下来考虑一下一个值如何移动。 对于一个值 $x$,它暂时没被删掉,假设它的下标是 $id$,有 $cnt$ 个 $a_i<id$,那么下一步它的下标会减 $cnt$,每一步都是如此。 我们找一个很靠右的区间,例如 $[10^{18},10^{18}+n-1]$,令 $L=10^{18}$,每次这个区间中的数下标都会减 $n$,直到遇到 $a_n$,在此之前这些数会不重不漏地覆盖右侧的所有数。当现存区间的左端点现在在 $a_i\sim a_{i+1}$ 间时,区间中会剩下 $i$ 个数,然后每个数下标都会减 $i$,所以它们会不重不漏地覆盖 $[a_1,L+n-1]$ 中的所有位置。 于是,对于一次询问 $x,k$,我们找到 $x$ 这个位置被覆盖的时间 $t$,那我们就求出了 $f^t(x)$,考虑求出 $f^{k-t}(f^t(x))$。 这个比较简单,我只需要查询 $f^t(x)$ 在 $t-k$ 时刻前在哪个位置即可。 显然我们需要特判 $x<a_1$。 以上过程可以用主席树维护,不知道会不会卡常,但是可以把查询离线下来再跑一遍,这样就只需要树状数组,可以轻易通过,时间复杂度 $O((n+q)\log n)$。 ### 代码 ```cpp #include<bits/stdc++.h> #define cint const int #define uint unsigned int #define cuint const unsigned int #define ll long long #define cll const long long #define ull unsigned long long #define cull const unsigned long long #define sh short #define csh const short #define ush unsigned short #define cush const unsigned short using namespace std; int read() { int x=0; char ch=getchar(); while(ch<'0'||ch>'9') { ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch-'0'); ch=getchar(); } return x; } void print(cll x) { if(x<10) { putchar(x+'0'); return; } print(x/10); putchar(x%10+'0'); } void princh(cll x,const char ch) { print(x); putchar(ch); } inline ll Div(cll x,cll y) { return (x+y-1)/y; } int n,q,a[500001]; ll ans[500001]; struct BIT{ int a[500001]; void clear() { for(int i=1;i<=n;++i)a[i]=0; } void add(cint x,cint y) { for(int i=x;i<=n;i+=(i&-i))a[i]+=y; } int ask(cint x) { int s=0; for(int i=x;i;i-=(i&-i))s+=a[i]; return s; } int find(int k) { int s=0,p=0; for(int i=1<<__lg(n);i;i>>=1) { if(p+i>n)continue; if(s+a[p+i]<k) { p+=i; s+=a[p]; } } return p+1; } }T; struct query{ int idx; ll x,k; }op[500001]; bool cmpx(query x,query y) { return (x.x>y.x); } bool cmpk(query x,query y) { return (x.k<y.k); } ll L=1e18,step; int main() { n=read(); q=read(); for(int i=1;i<=n;++i) { a[i]=read(); T.add(i,1); } for(int i=1;i<=q;++i) { op[i].idx=i; op[i].x=read(); op[i].k=read(); ans[i]=(op[i].x<a[1]?op[i].x:-1); } sort(op+1,op+q+1,cmpx); for(int i=1,j=n;i<=q&&j;) { cll d=Div(L-a[j],j),k=d*j; while(i<=q&&op[i].x>=L-k) { if(ans[op[i].idx]==-1) { op[i].k=step+Div(L-op[i].x,j)-op[i].k; op[i].x=T.find(op[i].x-(L-j*Div(L-op[i].x,j))+1); } ++i; } while(j&&a[j]>=L) { cint tmp=T.find(a[j]-L+1); T.add(tmp,-1); --j; } L-=k; step+=d; } sort(op+1,op+q+1,cmpk); L=1e18; step=0; T.clear(); for(int i=1;i<=n;++i)T.add(i,1); for(int i=1,j=n;i<=q&&j;) { cll d=Div(L-a[j],j),k=d*j; while(i<=q&&op[i].k<=step+d) { if(ans[op[i].idx]==-1) { ans[op[i].idx]=L-1+T.ask(op[i].x)-(op[i].k-step)*j; } ++i; } while(j&&a[j]>=L) { cint tmp=T.find(a[j]-L+1); T.add(tmp,-1); --j; } L-=k; step+=d; } for(int i=1;i<=q;++i) { princh(ans[i],'\n'); } return 0; } ```