zoukankan      html  css  js  c++  java
  • CF698F Coprime Permutation

    一、题目

    点此看题

    二、解法

    网上大多数题解我都不满意,但是这里要强推 Qiuly 大佬的题解啊,讲得是真的好。虽然本题的关键步骤我已经走出来了,但是为什么我难以继续走下去?为什么我难以完整地想出一道题呢?

    首先考虑怎么判定一个已知的排列是否合法,然后我自己想出了一个数链理论:我们选取每个质数作为基数,然后取出 \(1\sim n\) 中这个质数的所有倍数构成数链,那么数链内部的顺序可以"调整",数链之间不能互换。举个例子,若 \(n=10\),那么我认为 \(2,4,6,8,10\) 应该在位置 \(2,4,6,8,10\) 上,它们内部顺序可以互换。

    上面的理论好像既不充分也不必要,我们考虑对它加以修正。考虑对于单独的数其实满足所有数链的限制就可以了,所以数交换的充要条件是两个数的质因数组合相同,所以我们可以统计 \(s_1[x]\) 表示质因数组合为 \(x\) 的数的出现次数,然后用阶乘就可以计算出方案数。

    上面的思考还是不够全面,因为对于 \(>\sqrt n\) 的质数,可能出现 \(\lfloor\frac{n}{p}\rfloor\) 相同的情况,那么就说明可以交换整个数链。那么我们统计 \(s_2[x]\) 表示 \(\lfloor\frac{n}{p}\rfloor=x\) 的质数个数,这里也是用阶乘计算出方案数。

    那么判断题目给出的部分排列是否合法的方法也就呼之欲出了,按照下面的步骤来吧:

    • 首先判断位置 \(i\) 和给定数 \(x\) 的质因数分解是否相同(除去最大质数),不同则无解。
    • 然后判断最大质数是否能交换数链。
    • 判断最大质数时间的映射关系,设 \(a[u]\) 表示 \(u\) 被谁替换了,\(b[u]\) 表示 \(u\) 替换了谁,维护它们两个即可。
    • 维护 \(s_1\)\(s_2\),方便最后计算方案数。

    方法就这样,我又没做出来题,总之现在就是非常后悔,非常后悔。

    #include <cstdio>
    #include <vector>
    #include <cstdlib>
    using namespace std;
    const int M = 1000005;
    const int MOD = 1e9+7;
    #define pb push_back
    #define gg {puts("0");exit(0);}
    int read()
    {
    	int x=0,f=1;char c;
    	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
    	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
    	return x*f;
    }
    int n,m,a[M],b[M],p[M],vis[M];vector<int> y[M];
    int fac[M],num[M],siz[M],s0[M],s1[M];
    /*
    a[u] : the u is repalced by a[u]
    b[u] : u replace b[u]
    */
    void sieve()
    {
    	fac[0]=1;
    	for(int i=1;i<=n;i++) num[i]=1;
    	for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%MOD;
    	for(int i=2;i<=n;i++) if(!vis[i])
    	{
    		p[++m]=i;num[i]=i;y[i].pb(i);
    		for(int j=i+i;j<=n;j+=i)
    			vis[j]=1,num[j]*=i,y[j].pb(i);
    	}
    	y[1].pb(1);siz[1]=1;
    	for(int i=2;i<=n;i++) siz[i]=n/i;
    	for(int i=1;i<=n;i++) s0[siz[i]]+=!vis[i],s1[num[i]]++;
    }
    signed main()
    {
    	n=read();sieve();
    	for(int i=1;i<=n;i++)
    	{
    		int x=read();
    		if(x==0) continue;
    		if(y[i].size()!=y[x].size()) gg
    		for(int j=0;j+1<y[i].size();j++)
    			if(y[i][j]!=y[x][j]) gg
    		int u=y[i].back(),v=y[x].back();
    		if(siz[u]!=siz[v]) gg
    		if(a[u] && a[u]!=v) gg
    		if(b[v] && b[v]!=u) gg
    		s0[siz[u]]-=!a[u];s1[num[x]]--;
    		a[u]=v;b[v]=u;
    	}
    	int ans=1;
    	for(int i=1;i<=n;i++)
    		ans=1ll*ans*fac[s0[i]]%MOD*fac[s1[i]]%MOD;
    	printf("%lld\n",ans);
    }
    
  • 相关阅读:
    csu 1503: 点弧之间的距离-湖南省第十届大学生计算机程序设计大赛
    Android MediaPlayer 和 NativePlayer 播放格式控制
    国内互联网企业奇妙招数
    [Oracle] Insert All神奇
    代码杂记
    R.layout.main connot be resolved 和R.java消失
    计算机安全篇(1)
    深入浅出谈开窗函数(一)
    PHP JSON_ENCODE 不转义中文汉字的方法
    c#indexof使用方法
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15805652.html
Copyright © 2011-2022 走看看