2020牛客暑期多校訓練營(第六場)J - Josephus Transform - 結論
阿新 • • 發佈:2020-10-07
Description
給定一個長度為 \(n\) 的排列和 \(m\) 次操作,每次操作被描述為 \((k,x)\) 表示對排列進行 \(x\) 次 k-約瑟夫變換,求末態排列。
Solution
設上次取出來的數字是第 pos 個(初態為 1),當前還剩下 cnt 個數字,下一個被選處理的數應當是當前剩下的所有數字中的第 (pos-1+k-1)%cnt+1 個。可以線上段樹上二分。
最後對所有求出的排列快速冪即可。
(注意一下置換乘法的方向)
#include <bits/stdc++.h> using namespace std; #define int long long const int N = 500005; #define lc p*2,l,(l+r)/2 #define rc p*2+1,(l+r)/2+1,r #define mid (l+r)/2 struct segment_tree { int a[4*N]; void pushup(int p) { a[p]=a[p*2]+a[p*2+1]; } void build(int p,int l,int r) { if(l==r) a[p]=1; else build(lc), build(rc), pushup(p); } void modify(int p,int l,int r,int pos,int key) { if(l==r) a[p]+=key; else if(pos<=mid) modify(lc,pos,key), pushup(p); else modify(rc,pos,key), pushup(p); } int bisect(int p,int l,int r,int k) { if(l==r) return l; if(a[p*2]>=k) return bisect(lc,k); else return bisect(rc,k-a[p*2]); } } seg; int jos[N]; void gen(int n,int k) { seg.build(1,1,n); int pos=1; for(int i=1;i<=n;i++) { int cnt=n-i+1; pos=(pos-1+k-1+cnt)%cnt+1; int val=seg.bisect(1,1,n,pos); seg.modify(1,1,n,val,-1); jos[i]=val; } } int buf[N]; void mul(int n,int *a,int *b) // a*=b { for(int i=1;i<=n;i++) buf[i]=a[b[i]]; for(int i=1;i<=n;i++) a[i]=buf[i]; } void qpow(int n,int k,int *a,int *b) { if(k==0) return; if(k&1) mul(n,a,b); mul(n,b,b); qpow(n,k/2,a,b); } int n,m,p[N]; signed main() { ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++) p[i]=i; for(int i=1;i<=m;i++) { int k,x; cin>>k>>x; gen(n,k); qpow(n,x,p,jos); } for(int i=1;i<=n;i++) cout<<p[i]<<" "; cout<<endl; return 0; }