• 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;
    }
    
    
  • 相关阅读:
    作业三
    作业二
    4.9上机作业
    4.2Java
    Java作业
    第四周上级作业
    第三周作业
    第一次上机练习
    [ZJOI2009]假期的宿舍
    [ZJOI2007]矩阵游戏
  • 原文地址:https://www.cnblogs.com/1024-xzx/p/13631382.html
Copyright © 2020-2023  润新知