2020牛客暑期多校训练营第六场Josephus Transform(二分+树状数组+置换群)

Josephus Transform

原题请看这里

题目描述:

给定长度为 n n 的排列 P P ( ( 初始 P P = = { \{ 1 , 2 , . . . , n 1,2,...,n } \} ) ) m m 次操作。每次操作可以用 ( k , x ) (k,x) 表示,代表执行 x x k k- 约瑟夫变换。请输出最后的排列。
k k- 约瑟夫变换表示:将排列 P P 排成一个环,从第一位开始逐个数数,将数到 k k 的元素删除,并添加到一个新的排列 P P' 中。然后继续从下一个数开始数数。重复上述操作,直到所有元素都被添加到 P P' 中, P P' 就是结果。例如 { \{ 1 , 2 , 3 , 4 , 5 1,2,3,4,5 } \} 在执行 3 3- 约瑟夫变换后可以得到 { \{ 3 , 1 , 5 , 2 , 4 3,1,5,2,4 } \}

输入描述:

第一行包含两个整数 n m n,m ( ( 1 1 \le n n m m \le 1 0 5 10 ^ 5 1 1 \le n n × \times m m \le 1 0 6 10 ^ 6 ) ) 接下来 m m 行每行包含两个整数 k k x x ( ( 1 1 \le k k \le n n 1 1 \le x x \le 1 0 9 10 ^ 9 ) ) ,表示一项操作 ( ( k k x x ) )

输出描述:

一行,打印 n n 个整数。

样例:

样例输入1:

5 1
3 1

样例输出1:

3 1 5 2 4

样例输入2:

5 2
3 3
2 3

样例输出2:

1 2 3 4 5

思路:

置换群!
又是置换群!!
怎么还是置换群!!!
为什么牛客多校有那么多置换群!!!!

前几场牛客置换群相关题解:
第二场 J u s t S h u f f l e Just Shuffle
第五场 B o g o S o r t Bogo Sort
手动模拟了一下,你就会发现变几次之后就回去了…不信你可以把题目给的样例手算试试看,直接就想到置换群…
第一时间想到暴力求,一看到 m m 的取值范围就放弃了(赛场上K题一直卡着)
作为牛客多校的特色置换群,这题与前几场的置换群题目稍有不同,需要使用树状数组(或线段树)来维护。
如果上一个被取出来的数字是 t t ,当前还剩 c n t cnt 个数,那么下一个被选出来的数字应该是剩下数字的第 ( t + k 2 ) (t+k-2) m o d mod c n t + 1 cnt+1 个,这时记录这个序列就需要树状数组(或线段树)来处理了,时间复杂度 O ( n l o g n ) O(nlogn) 。然后可以用类似于快速幂的方法来做x次k-约瑟夫变换,时间复杂度 O ( n l o g x ) O(nlogx)
处理方法:二分+树状数组+置换群
时间复杂度: O ( n m ( l o g n + l o g x ) ) O(nm(logn+logx))
(呜呜呜写的好烂,欢迎 d a l a o dalao 前来指正和提点)
线段树写法

A C AC C o d e Code :

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
int n,q,k,x,sum[MAXN],a[MAXN],b[MAXN],c[MAXN],t,l,r;
int find(int x){
    int ans=0;
    for(int i=x;i;i-=(i&(-i)))
        ans+=sum[i];
    return ans;
}
void use(int mid){
    for(int i=mid;i<=n;i+=(i&(-i)))
        sum[i]++;
}
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;i++)
		a[i]=i;
    while(q--){
        scanf("%d%d",&k,&x);
        for(int i=1;i<=n;i++) sum[i]=0;//memset会超时......只能手动清零
        t=1;
        for(int i=1;i<=n;i++){
            t=(t+k-2)%(n-i+1)+1,l=1,r=n;
            while(l<r){
                int mid=l+r>>1;
                if(mid-find(mid)>=t) r=mid;
                else l=mid+1;
            }//二分
            b[i]=l;
            use(l);
        }
        while(x){
            if(x&1){
                for(int i=1;i<=n;i++)
					c[i]=a[b[i]];
                for(int i=1;i<=n;i++)
					a[i]=c[i];
            }
            for(int i=1;i<=n;i++)
				c[i]=b[b[i]];
            for(int i=1;i<=n;i++)
				b[i]=c[i];
            x>>=1;
        }
    }
    for(int i=1;i<=n;i++)
        printf("%d ",a[i]);
    puts("");
}

猜你喜欢

转载自blog.csdn.net/s260127ljy/article/details/107643332