2020牛客暑期多校训练营(第六场) Josephus Transform
题解:
这个和之前牛客的一个题目很像
2020牛客暑期多校训练营(第二场) [Just Shuffle]
会做那个之后,这个你只要发现这个找操作就是一种置换,所以可以找到这个置换数组,这个应该是这个题目的难点,可以用二分+树状数组找到这个置换数组,然后就是多次进行置换即可。
严格来说这个题目其实比第二场的这个题目简单很多,但是时间不够,没写出来。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+10;
int p[maxn],a[maxn],c[maxn],n,f[maxn],cnt[maxn],b[maxn];
int lowbit(int x){
return x&(-x);
}
void updata(int i,int k){ //??i????????k
while(i <= n){
c[i] += k;
i += lowbit(i);
}
}
int getsum(int i){ //?¡§?A[1 - i]????
int res = 0;
while(i > 0){
res += c[i];
i -= lowbit(i);
}
return res;
}
int Find(int x){
return x==f[x]?x:f[x]=Find(f[x]);
}
int main(){
int m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) b[i]=i;
while(m--){
int k,x;
scanf("%d%d",&k,&x);
for(int i=1;i<=n;i++) c[i]=0,f[i]=i,cnt[i]=0;
for(int i=1;i<=n;i++) updata(i,1);
int l = 1;
for(int i=1;i<=n;i++){
int now = n-i+1;
int sum = getsum(n)-getsum(l-1),cur = k;
if(now<k) cur = k%now;
if(!cur) cur+=now;
if(sum<cur) cur = cur-sum,l = 1;
int lc = 1,rc = n,res = getsum(l-1),pos=0;
while(lc<=rc) {
int mid = (lc + rc) >> 1;
int sum1 = getsum(mid) - res;
if (sum1 >= cur) rc = mid - 1,pos=mid;
else lc = mid + 1;
}
a[i]=pos;
updata(pos,-1);
l = pos;
if(Find(i)!=Find(pos)) f[i]=pos;
}
// for(int i=1;i<=n;i++) printf("a[%d]=%d\n",i,a[i]);
for(int i=1;i<=n;i++) cnt[Find(i)]++;
for(int i=1;i<=n;i++) {
if (!cnt[i]) continue;
int len = cnt[i], w = i, now = i;
int num = x % len;
for (int j = 1; j <= num; j++) w = a[w];
for (int j = 1; j <= len; j++) {
p[now] = b[w];
w = a[w], now = a[now];
}
}
for(int i=1;i<=n;i++) b[i]=p[i];
// for(int i=1;i<n;i++) printf("%d ",p[i]);
// printf("%d\n",p[n]);
}
for(int i=1;i<n;i++) printf("%d ",p[i]);
printf("%d\n",p[n]);
}
/*
4 1
2 1
*/