BZOJ 2613 Poi2003 Shuffle 数论

42 篇文章 0 订阅

题目大意:给定一个长度为 n 的置换b和一个正整数 k , 求一个置换a,使得 ak=b

要做这个题首先我们需要知道 ak 是什么
想象一个长度为 L 的循环,如果我们将这个循环求k次方,我们将会得到 Gcd(L,k) 个长度为 LGcd(L,k) 的循环
那么现在我们将 b 分解成循环,假如现在我们得到了一个长度为L的循环,那么由之前的结论可以得到 L=LGcd(L,k)
容易证明存在一个最小的 L 满足这个L是所有合法的 L 的约数,且这个L满足对于 L 的任意一个质因子 p p L 中的次数等于p L 中的次数加上 p k中的次数
于是我们找到这个最小的 L ,将LGcd(L,k)个长度为 L 的循环合并成一个循环即可
BZ上没有SPJ,建议去main上交
回头写个SPJ去

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 1001001
using namespace std;
int n,k;
int a[M],ans[M];
bool v[M];
vector<int> *stack[M];int top;
bool Compare(vector<int> *v1,vector<int> *v2)
{
    return v1->size() < v2->size() ;
}
void Get_Circulation(int x,vector<int> *vec)
{
    while(1)
    {
        if(v[x]) return ;
        vec->push_back(x);
        v[x]=true;x=a[x];
    }
}
int Get_L(int x)
{
    int i,k=::k,re=1;
    for(i=2;i*i<=x;i++)
        if(x%i==0)
        {
            while(x%i==0)
                x/=i,re*=i;
            while(k%i==0)
                k/=i,re*=i;
        }
    if(x!=1)
    {
        re*=x;
        while(k%x==0)
            k/=x,re*=x;
    }
    return re;
}
int main()
{
    int i,j,k;
    cin>>n>>::k;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=1;i<=n;i++)
        if(!v[i])
            Get_Circulation(i,stack[++top]=new vector<int>);
    sort(stack+1,stack+top+1,Compare);
    for(i=1;i<=top;)
    {
        static int a[M];
        int L=Get_L(stack[i]->size());
        int temp=__gcd(L,::k);
        for(j=0;j<temp;j++)
            for(k=0;k<(signed)stack[i+j]->size();k++)
                a[(j+(long long)k*::k)%L]=(*stack[i+j])[k];
        for(j=0;j<L;j++)
            ans[a[j]]=a[(j+1)%L];
        i+=temp;
    }
    for(i=1;i<=n;i++)
        printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值