置换群

对于一个大小为$n$的循环,走$k$步之后分解成$gcd(n,k)$个循环

现在相当于我们知道$a = gcd(n,k)$和$k$,分别合成最初的循环。

问题在于找出$n$,我们构造一个最小的$n$,这个$n$满足包含所有在$a$中出现的质因子的次数加上质因子在$k$中的出现次数

那么暴力枚举因数即可

然后合并循环,按照手上模拟即可。

bzoj2613_置换群bzoj2613_质因子_02
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int n, l, top;
vector<int> *st[maxn];
int vis[maxn], b[maxn], tmp[maxn], ans[maxn];
bool cmp(vector<int> *a, vector<int> *b) {
    return a -> size() < b -> size();
}
int cal(int n) {
//    puts("in");
    int ret = 1, t = l;
    for(int i = 2; i * i <= n; ++i) if(n % i == 0) {
        while(n % i == 0) {
            n /= i;
            ret *= i;
        }
        while(t % i == 0) {
            t /= i;
            ret *= i;
        }
    }
    if(n != 1) {
        ret *= n;
        while(t % n == 0) {
            t /= n;
            ret *= n;
        }
    }
    return ret;
}
int main() {
    scanf("%d%d", &n, &l);
    for(int i = 1; i <= n; ++i) scanf("%d", &b[i]);
    for(int i = 1; i <= n; ++i) if(!vis[i]) {
        int p = i;
        st[++top] = new vector<int>;
        while(!vis[p]) {
            st[top] -> push_back(p);
            vis[p] = 1;
            p = b[p];
        }
    } 
    sort(st + 1, st + top + 1, cmp);
    for(int i = 1; i <= top; ) {
        int t = cal(st[i] -> size());
        int len = __gcd(t, l);
//        printf("size = %d t = %d len = %d\n", st[i] -> size(), t, len);
        for(int j = 0; j < len; ++j) 
            for(int k = 0; k < st[i + j] -> size(); ++k)
                tmp[(j + 1LL * l * k) % t] = (*st[i + j])[k];
        for(int j = 0; j < t; ++j) ans[tmp[j]] = tmp[(j + 1) % t];
        i += len;
//        puts("fffffff");
    } 
    for(int i = 1; i <= n; ++i) printf("%d\n", ans[i]);
    return 0;
}
View Code