Josephus Transform
题目描述:
给定长度为
n
n
n的排列
P
P
P
(
(
(初始
P
P
P
=
=
=
{
\{
{
1
,
2
,
.
.
.
,
n
1,2,...,n
1,2,...,n
}
\}
}
)
)
)和
m
m
m次操作。每次操作可以用
(
k
,
x
)
(k,x)
(k,x)表示,代表执行
x
x
x次
k
−
k-
k−约瑟夫变换。请输出最后的排列。
k
−
k-
k−约瑟夫变换表示:将排列
P
P
P排成一个环,从第一位开始逐个数数,将数到
k
k
k的元素删除,并添加到一个新的排列
P
′
P'
P′ 中。然后继续从下一个数开始数数。重复上述操作,直到所有元素都被添加到
P
′
P'
P′ 中,
P
′
P'
P′ 就是结果。例如
{
\{
{
1
,
2
,
3
,
4
,
5
1,2,3,4,5
1,2,3,4,5
}
\}
} 在执行
3
−
3-
3−约瑟夫变换后可以得到
{
\{
{
3
,
1
,
5
,
2
,
4
3,1,5,2,4
3,1,5,2,4
}
\}
}
输入描述:
第一行包含两个整数 n , m n,m n,m ( ( ( 1 1 1 ≤ \le ≤ n n n, m m m ≤ \le ≤ 1 0 5 10 ^ 5 105, 1 1 1 ≤ \le ≤ n n n × \times × m m m ≤ \le ≤ 1 0 6 10 ^ 6 106 ) ) )接下来 m m m行每行包含两个整数 k k k, x x x ( ( ( 1 1 1 ≤ \le ≤ k k k ≤ \le ≤ n n n, 1 1 1 ≤ \le ≤ x x x ≤ \le ≤ 1 0 9 10 ^ 9 109 ) ) ),表示一项操作 ( ( ( k k k, x x x ) ) )
输出描述:
一行,打印 n 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
JustShuffle
第五场
B
o
g
o
S
o
r
t
Bogo Sort
BogoSort
手动模拟了一下,你就会发现变几次之后就回去了…不信你可以把题目给的样例手算试试看,直接就想到置换群…
第一时间想到暴力求,一看到
m
m
m的取值范围就放弃了(赛场上K题一直卡着)
作为牛客多校的特色置换群,这题与前几场的置换群题目稍有不同,需要使用树状数组(或线段树)来维护。
如果上一个被取出来的数字是
t
t
t,当前还剩
c
n
t
cnt
cnt个数,那么下一个被选出来的数字应该是剩下数字的第
(
t
+
k
−
2
)
(t+k-2)
(t+k−2)
m
o
d
mod
mod
c
n
t
+
1
cnt+1
cnt+1 个,这时记录这个序列就需要树状数组(或线段树)来处理了,时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。然后可以用类似于快速幂的方法来做x次k-约瑟夫变换,时间复杂度
O
(
n
l
o
g
x
)
O(nlogx)
O(nlogx)
处理方法:二分+树状数组+置换群
时间复杂度:
O
(
n
m
(
l
o
g
n
+
l
o
g
x
)
)
O(nm(logn+logx))
O(nm(logn+logx))
(呜呜呜写的好烂,欢迎
d
a
l
a
o
dalao
dalao前来指正和提点)
线段树写法
A C AC AC C o d e Code 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("");
}