Josephus Transform【置换群+快速幂+线段树】

题意

初始序列 (P={1,2,dots ,n}),有 (m) 个操作,每次操作以 ((k,x)) 的形式给出,表示在上一次操作的结果的基础上进行 (x) 次以 (k) 为步长的约瑟夫环的跳跃操作。并依次将选中的数拿出,组成一个新的序列。求出最终得到的序列。

(1leq n,m leq 10^5,1leq n imes m leq 10^6,1leq k leq n,1leq x leq 10^9)

题目链接:https://ac.nowcoder.com/acm/contest/5671/J

分析:

每次进行操作形成新序列时,其实就相当于进行置换。因此,只要求出一次置换的排列,其它相同步长的跳跃,都是一样的。

对于当前选择的位置 (pos) ,假设当前原序列中还剩余 (num) 个数,那么下一个选择的数在由剩余数组成的排列中的位置为 ((pos-1+k-1)\%num+1)。而要求出剩余的数的个数和剩余序列中的第 (x) 个数的位置,都可以借助权值线段树来求。而考虑到置换满足结合律并且 (x) 较大,因此可以用快速幂处理。注意在求置换时,是对位置求。

复杂度:(O(nm·logn))

代码

#include <bits/stdc++.h>

using namespace std;
const int N=1e5+5;
int tree[N<<2];
int a[N],ans[N],c[N],d[N];
void pushup(int rt)
{
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}
void build(int l,int r,int rt)
{
    tree[rt]=0;
    if(l==r)
    {
        tree[rt]=1;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void update(int l,int r,int pos,int rt)
{
    if(l==r)
    {
        tree[rt]=0;
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) update(l,mid,pos,rt<<1);
    else update(mid+1,r,pos,rt<<1|1);
    pushup(rt);
}
int query(int l,int r,int L,int R,int rt)
{
    if(L<=l&&r<=R)
        return tree[rt];
    int mid=(l+r)>>1,res=0;
    if(L<=mid) res+=query(l,mid,L,R,rt<<1);
    if(R>mid) res+=query(mid+1,r,L,R,rt<<1|1);
    return res;
}
int ask(int l,int r,int k,int rt)
{
    if(l==r) return l;
    int tmp=tree[rt<<1],mid=(l+r)>>1;
    if(k<=tmp) return ask(l,mid,k,rt<<1);
    else return ask(mid+1,r,k-tmp,rt<<1|1);
}
void solve(int k,int n)
{
    int cnt=0,p=k;
    a[++cnt]=k;//对位置求
    update(1,n,k,1);
    while(++cnt<=n)
    {
        int s=query(1,n,1,p,1);
        int x=(s+k-1)%tree[1]+1;
        p=ask(1,n,x,1);
        a[cnt]=p;//对位置求
        update(1,n,p,1);
    }
}
void change(int n,int x[],int y[])
{
    for(int i=1;i<=n;i++)
        d[i]=x[y[i]];
    for(int i=1;i<=n;i++)
        x[i]=d[i];
}
void power(int n,int b)
{
    for(int i=1;i<=n;i++) c[i]=i;
    while(b)
    {
        if(b&1) change(n,c,a);
        change(n,a,a);
        b>>=1;
    }
}
int main()
{
    int n,m,k,x;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) ans[i]=i;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&k,&x);
        build(1,n,1);
        solve(k,n);
        power(n,x);
        change(n,ans,c);
    }
    for(int i=1;i<=n;i++)
        printf("%d%c",ans[i],i==n?'
':' ');
    return 0;
}

原文地址:https://www.cnblogs.com/1024-xzx/p/13631382.html