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);
    }
    
  • 相关阅读:
    [ Algorithm ] N次方算法 N Square 动态规划解决
    [ Algorithm ] LCS 算法 动态规划解决
    sql server全文索引使用中的小坑
    关于join时显示no join predicate的那点事
    使用scvmm 2012的动态优化管理群集资源
    附加数据库后无法创建发布,error 2812 解决
    浅谈Virtual Machine Manager(SCVMM 2012) cluster 过载状态检测算法
    windows 2012 r2下安装sharepoint 2013错误解决
    sql server 2012 数据引擎任务调度算法解析(下)
    sql server 2012 数据引擎任务调度算法解析(上)
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15805652.html
Copyright © 2011-2022 走看看