codeforces 698F 数学

题意:构造一个1到n的排列 a1,a2,......an 满足对于任意i,j满足 1<=i<j<=n ai,aj 互质当且仅当i,j互质。a中一些元素已知,求可以构造的排列个数。

对于两个质数p1,p2,如果有这两个质数因子的数的个数相等(既n/p1=n/p2)那么可以将所有数的质因子中出现的这两个质数交换。

如果两个数包含相同的质因子,那么可以将两个数交换。

如果第i个数ai已知:
如果ai质因子个数和i的质因子个数不等,无解。
如果ai的第j个质因子和i的第j个质因子出现次数不等,无解。
注意设ai有pi个质因子,这里只需要判断前pi-1个质因子是否相等和第pi个质因子出现次数是否相等即可。因为前pi-1个质因子小于根号n,对于小于根号n的两个质数p1,p2:n/p1和n/p2不等。
如果最后一个质因子已经有对应的质因子,无解。

最后阶乘统计答案即可。

注意处理1的问题。

#include 
using namespace std;
#define mod 1000000007
#define ll long long
#define N 1100000
int n;
ll jc[N],ans;
int a[N],num[N],num1[N],pos[N],mul[N];
int pre[N],nex[N];
vector<int>v[N];
void quit(){puts("0");exit(0);}
int main()
{
    //freopen("tt.in","r",stdin);
    scanf("%d",&n);
    jc[0]=1;
    for(int i=1;i<=n;i++)jc[i]=jc[i-1]*i%mod;
    for(int i=1;i<=n;i++)mul[i]=1;
    for(int i=2,pre=0;i<=n;i++)
        if(!v[i].size())
        {
            num[n/i]++;pos[i]=n/i;
            for(int j=i;j<=n;j+=i)
                v[j].push_back(i),mul[j]*=i;
        }
    for(int i=1;i<=n;i++)num1[mul[i]]++;
    num[1]++;pos[1]=1;
    v[1].push_back(1);

    for(int i=1,x,y;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(a[i]==0)continue;
        num1[mul[a[i]]]--;
        if(v[a[i]].size()!=v[i].size())quit();
        for(int j=0;j1;j++)
            if(v[a[i]][j]!=v[i][j])quit();
        x=v[a[i]].back();y=v[i].back();
        if(pos[x]!=pos[y])quit();
        if(pre[x]&&pre[x]!=y)quit();
        if(nex[y]&&nex[y]!=x)quit();
        if(!pre[x]&&!nex[y])num[pos[x]]--;
        pre[x]=y;nex[y]=x;
    }
    ans=1;
    for(int i=1;i<=n;i++)ans=ans*jc[num1[i]]%mod;
    for(int i=1;i<=n;i++)ans=ans*jc[num[i]]%mod;
    printf("%I64d\n",ans);
    return 0;
}

你可能感兴趣的:(数学)