2020牛客暑期多校训练营(第六场)J-Josephus Transform 群置换,约瑟夫环
J-Josephus Transform
题意
对排列\(P=\left\{ 1,2,\dots,n \right\}\)做\(m\)次操作,每次操作给一对数\(k,x\),表示对\(P\)做\(x\)次步长为\(k\)的约瑟夫变换,求出最终的\(P\)排列。
分析
先考虑如何求一个\(k\)-约瑟夫变换,注意每次取出的数字是剩下的几个数字中的第几个是可以算出来的,设上一次被取出的数字是当时的第\(pos\)个(初始设为 \(1\)),当前还剩下 \(cnt\) 个数字,那么下一 个被选出来的数应该是当前剩下的所有数字中的第 \((pos-1+k-1) \% cnt + 1\) 个,用线段树来求编号为\(k\)的数,对数字从\(1\sim n\)建线段树,初始值都为\(1\),被取出后置为\(0\),编号为\(k\)的数即为前缀和等于\(k\)的位置,可以在线段树上二分求出。
求出\(k\)-约瑟夫变换后,就可以对\(P\)做置换了,群置换是满足结合律的,所以可以用快速幂来算,设\(k\)-约瑟夫变换排列为\(a\),把置换看做乘法,那么对\(P\)做\(x\)次置换即为\(P \cdot a^x\),快速幂求出\(a^x\),然后再用\(a^x\)对\(P\)做一次置换就可以了。
Code
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<sstream>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int mod=1e9+7;
const int N=1e6+10;
const int inf=1e9;
int n,m;
int tr[N<<2],a[N],A[N],B[N],tmp[N];
void bd(int l,int r,int p){
if(l==r) return tr[p]=1,void();
int mid=l+r>>1;
bd(lson);bd(rson);
tr[p]=tr[p<<1]+tr[p<<1|1];
}
void up(int x,int l,int r,int p){
if(l==r) return tr[p]=0,void();
int mid=l+r>>1;
if(x<=mid) up(x,lson);
else up(x,rson);
tr[p]=tr[p<<1]+tr[p<<1|1];
}
int find(int x,int l,int r,int p){
if(l==r) return l;
int mid=l+r>>1;
if(tr[p<<1]<x) return find(x-tr[p<<1],rson);
else return find(x,lson);
}
void change(int k){
bd(1,n,1);
int pos=1;
rep(i,1,n){
pos=(pos+k-2)%(n-i+1)+1;
int x=find(pos,1,n,1);
a[i]=x;
up(x,1,n,1);
}
}
void mul(int a[],int b[]){
rep(i,1,n) tmp[i]=a[b[i]];
rep(i,1,n) a[i]=tmp[i];
}
void ksm(int x){
rep(i,1,n) B[i]=i;
while(x){
if(x&1) mul(B,a);
x>>=1;
mul(a,a);
}
}
int main(){
//ios::sync_with_stdio(false);
//freopen("in","r",stdin);
scanf("%d%d",&n,&m);
rep(i,1,n) A[i]=i;
while(m--){
int k,x;
scanf("%d%d",&k,&x);
change(k);
ksm(x);
mul(A,B);
}
rep(i,1,n) printf("%d%c",A[i],i==n?'\n':' ');
return 0;
}