题目
长为n(n<=1e5)的排列,进行m(m<=1e5)次操作,保证n*m<=1e6
第i次操作,给定(ki,xi),要求将现在的排列,
按约瑟夫环ki步取数的方式,取出一个新排列,重复xi次,
第i次的操作是在第i-1次结束后的排列上进行操作的,初始排列是[1,2,..,n]
求最终排列
思路来源
https://blog.csdn.net/liufengwei1/article/details/107615435
题解
先用线段树二分求出一次约瑟夫的新排列,考虑用线段树二分实现,
设上一次取的位置为pos,则要么在[pos,n]里二分,要么在[1,pos]里二分,
线段树二分[1,x]里第一个满足前缀sumx为rank的位置,
置换快速幂,则考虑,第1,2,3..,n个位置,一次的映射是怎样的,
然后将乘号理解成[],因为映射(置换)也是可结合的,搞出x次之后的映射,
再对下标对应的值转移到的位置进行赋值操作,
这里写的有点搓,进行了四次赋数组值的运算
也有更省时的做法,首先,线段树二分不可省,
然后考虑出对每个置换循环节弄出一个环,
x次置换就是在环上往后转x%cyc个位置,其中cyc为环长,
这样就省去了快速幂的时间
代码
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=1e5+10;
int n,m,k,x,now[N],nex[N],tmp[N],ans[N],to[N],pos;
struct segtree{
int n;
struct node{int l,r,v;}e[N<<2];
#define l(p) e[p].l
#define r(p) e[p].r
#define v(p) e[p].v
void up(int p){v(p)=v(p<<1)+v(p<<1|1);}
void bld(int p,int l,int r){
l(p)=l;r(p)=r;
if(l==r){v(p)=1;return;}
int mid=l+r>>1;
bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
up(p);
}
void init(int _n){n=_n;bld(1,1,n);}
void chg(int p,int x,int v){
if(l(p)==r(p)){v(p)=v;return;}
int mid=l(p)+r(p)>>1;
chg(p<<1|(x>mid),x,v);
up(p);
}
int cnt(int p,int ql,int qr){
if(ql<=l(p)&&r(p)<=qr)return v(p);
int mid=l(p)+r(p)>>1,res=0;
if(ql<=mid)res+=cnt(p<<1,ql,qr);
if(qr>mid)res+=cnt(p<<1|1,ql,qr);
return res;
}
int kth(int p,int rk){
if(l(p)==r(p))return l(p);
if(v(p<<1)>=rk)return kth(p<<1,rk);
return kth(p<<1|1,rk-v(p<<1));
}
}seg;
void ksm(int *ans,int *x,int w){//倍增x^w
for(int i=1;i<=n;++i){
ans[i]=i;
}
for(;w;w>>=1){
if(w&1){
for(int i=1;i<=n;++i){
tmp[i]=ans[x[i]];//ans=ans*x
}
for(int i=1;i<=n;++i){
ans[i]=tmp[i];
}
}
for(int i=1;i<=n;++i){
tmp[i]=x[x[i]];//x=x*x
}
for(int i=1;i<=n;++i){
x[i]=tmp[i];
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
now[i]=i;
}
for(int i=1;i<=m;++i){
scanf("%d%d",&k,&x);
seg.init(n);
int c=0;
pos=k;
seg.chg(1,pos,0);
nex[++c]=now[pos];
for(int j=n-1;j;j--){//还剩j个人没被安排 往后走nk步
int nk=(k%j==0?j:k%j),bac=seg.cnt(1,pos,n),fro=j-bac;
if(bac>=nk){//后面back个 从后一半找
pos=seg.kth(1,fro+nk);
}
else{//后面不够 从前一半找
pos=seg.kth(1,nk-bac);
}
seg.chg(1,pos,0);
nex[++c]=now[pos];
}
for(int j=1;j<=n;++j){
to[now[j]]=j;
}
for(int j=1;j<=n;++j){
ans[j]=to[nex[j]];//映射序列ans
}
ksm(nex,ans,x);//x次映射序列nex
for(int j=1;j<=n;++j){
ans[j]=now[nex[j]];
}
for(int j=1;j<=n;++j){
now[j]=ans[j];
}
}
for(int i=1;i<=n;++i){
printf("%d%c",now[i]," \n"[i==n]);
}
return 0;
}