线段树、树状数组

建立长度为 $n$,初始所有位置的值为 $1$ 的值域树状数组。将上次出列元素的排名(树状数组中比该元素小的值的数量 $+1$)记为 $las$,初始化 $las=1$。

对于第 $i$ 次出列,从当前排名为 $las$ 的元素向后找 $k-1$ 个元素(因为排名为 $las$ 的元素也报数了)就是本次出列的元素。因为在环上,直接找排名为 $(las+k-2) \bmod (n-i+1) +1$ 的元素编号,使用这个元素更新 $las$ 即可。

使用线段树的做法可以用线段树上二分替代树状数组上倍增。

时间复杂度 $O(n \log n)$。

#include<bits/stdc++.h>
using namespace std;
int n,k,ans,c[1'000'005];
void add(int x,int y){while(x<=n) c[x]+=y,x+=x&-x;}
int sum(int x){
    int ret=0;
    while(x) ret+=c[x],x-=x&-x;
    return ret;
}
int frank(int x){
    int ret=0;
    for(int i=1<<__lg(n);i;i>>=1) if(ret+i<=n&&c[ret+i]<x) x-=c[ret+=i];
    return ret+1;
}
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) c[i]=i&-i;
    for(int i=1;i<=n;i++){
        static int las=1;
        ans=frank((las+k-2)%(n-i+1)+1);
        las=sum(ans),add(ans,-1);
    }
    printf("%d\n",ans);
    return 0;
}
最后修改:2024 年 09 月 03 日
如果觉得我的文章对你有用,请随意赞赏