2020牛客多校六 J. Josephus Transform (线段树求约瑟夫变换+置换群的幂)

题意:给出一个长度为 n 的排列,初始序列为 1 , 2 , 3 … , n。有 m 次操作,每次操作表示为 ( k , x ) ,即进行 x 次 k-约瑟夫变换(一次k变换表示取出的顺序),求最终排列。

题解:线段树求约瑟夫变换+置换群的幂
参考的大佬博客

对于每一次操作,我们只要求出一次k-约瑟夫变换,x次的话就是该置换的x次幂,O(n)求一下即可。

如何求一次的k-约瑟夫变换呢?pos = (pos - 1 + k - 1) % cnt + 1; 求得取出元素在剩余序列的位置,然后在线段树中查询,查询的时候顺便更新一下。这样就是O(nlogn)。

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
#include<vector>
#include<fstream>
#include<set>
#include<map>
#include<sstream>
#include<iomanip>
#define ll long long
using namespace std;
const int MAXN = 1e5 + 5;
int sum[MAXN << 2];
struct Node {
    int l, r;
    int mid() { return (l + r) >> 1; }
} tree[MAXN << 2];
void build(int l, int r, int rt) {
    tree[rt].l = l;
    tree[rt].r = r;
    if (l == r) {
        sum[rt] = 1;
        return;
    }
    int m = tree[rt].mid();
    build(l, m, rt << 1);
    build(m + 1, r, rt << 1 | 1);
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
int query(int rt, int pos) {
    sum[rt]--;
    if (tree[rt].l == tree[rt].r) return tree[rt].l;
    if (sum[rt << 1] >= pos) return query(rt << 1, pos);
    else return query(rt << 1 | 1, pos - sum[rt << 1]);
}
int p[MAXN], temp[MAXN], ans[MAXN], n;
bool vis[MAXN];
void solve(int t) {   //O(n)实现置换群p的t次幂
    memset(vis, false, n + 5);
    for (int i = 1; i <= n; i++) {
        if (vis[i])
            continue;
        vector<int> circle;
        int pos = i;
        while (!vis[pos]) {
            circle.push_back(pos);
            vis[pos] = true;
            pos = p[pos];
        }
        int sz = circle.size();
        for (int i = 0; i < sz; i++)
            temp[circle[i]] = ans[circle[(i + t) % sz]];
    }
    for (int i = 1; i <= n; i++) ans[i] = temp[i];
}
int m, k, x;
int main() {
	scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) ans[i] = i;
    while (m--) {
        build(1, n, 1);
        scanf("%d%d", &k, &x);
        int pos = 1, cnt = n;
        for (int i = 1; i <= n; i++) {
            pos = (pos - 1 + k - 1) % cnt + 1;   //取出元素在剩余序列的位置
            p[i] = query(1, pos);
            //cout << p[i] << endl;
            cnt--;
        }
        solve(x);
    }
    for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
	return 0;
}
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页