地址:https://codeforces.com/contest/1174/problem/E
思路:dp 官方题解https://codeforces.com/blog/entry/67388
我们称一个好的置换中的第一个元素为s。那么,s必须有最大可能的质因数个数。此外,每次在移动前缀时更改gcd时,必须只从其中删除一个素数除数。
这样,可以保证我们有尽可能多的不同的gcds。现在,关于s有两个重要的意见:
#1:s=2^x*3^y,换句话说,s只有2和3两个质因子,那是因为如果s有某个素因子 (5,7,11,...),你至少可以把它换成2^2。这样,就会有更多质因数。
# 2:y <=1。这是因为如果s=2^x*3^y,且y≥2,那么就可以用2^(x+3)*3^(y - 2)来代替它(除以9,再乘以8),这样就会得到更多的素数因子。
创建dp[i][x][y],也就是从列表下标1->i时的良好排列的方法的数量,它的gcd就是2^x*3^y。
让f (x, y) = n/(2^x∗3^y)。它表示n中 2^x*3^y的倍数个数。有3种转变方式
1.添加2^x*3^y的倍数。这样,gcd就不会改变。可以添加f(x,y)个数,但是已经添加了其中的i,所以:
dp[i+1][x][y]=dp[i+1][x][y]+dp[i][x][y]*(f(x,y) - i)
2.把x减1。为此,可以添加一个2^(x - 1)*3^y的倍数,而2^x*3^y是其的倍数,因此:
dp[i+1][x - 1][y]=dp[i+1][x - 1][y]+dp[i][x][y]*(f(x−1,y)−f(x,y)).
3.把y减1。为此,可以添加一个2^x*3^(y-1)的倍数,而2^x*3^y是其的倍数,因此:
dp[i+1][x][y−1]=dp[i+1][x][y−1]+dp[i][x][y]*(f(x,y−1)−f(x,y)).
令p=log2(n)。可以从2^p开始,所以dp[1][p][0]=1。另外,如果2^(x-1)*3≤n,可以从2^(x-1)*3开始,所以dp[1][p-1][1]=1。
因为到末尾是gcd肯定为1,所以答案是dp[n][0][0]。
Code :
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAX_N=1e6+5;
const LL MOD=1e9+7;
int n;
int dp[MAX_N][21][2];
int f(int x,int y){
int tmp=(1<<x);
if(y) tmp*=3;
return n/tmp;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
int p=0;
while ((1<<p)<=n)
++p;
--p;
dp[1][p][0]=1;
if((1<<(p-1))*3<=n) dp[1][p-1][1]=1;
for(int i=1;i<n;++i)
for(int x=0;x<=p;++x)
for(int y=0;y<=1;++y)
{
dp[i+1][x][y]=(dp[i+1][x][y]+(LL)dp[i][x][y]*(f(x,y)-i))%MOD;
if(x) dp[i+1][x-1][y]=(dp[i+1][x-1][y]+(LL)dp[i][x][y]*(f(x-1,y)-f(x,y)))%MOD;
if(y) dp[i+1][x][y-1]=(dp[i+1][x][y-1]+(LL)dp[i][x][y]*(f(x,y-1)-f(x,y)))%MOD;
}
cout<<dp[n][0][0]<<endl;
return 0;
}
Code 优化空间:
#include<iostream>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAX_N=1e6+5;
const LL MOD=1e9+7;
int n;
LL dp[2][21][2];
int f(int x,int y){
int tmp=(1<<x);
if(y) tmp*=3;
return n/tmp;
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
int p=0;
while ((1<<p)<=n)
++p;
--p;
dp[1][p][0]=1;
if((1<<(p-1))*3<=n) dp[1][p-1][1]=1;
int u,v;
for(int i=1;i<n;++i)
for(int x=0;x<=p;++x)
{
u=i%2; v=(i+1)%2;
dp[v][x][0]=dp[v][x][1]=0;
for(int y=0;y<=1;++y)
{
dp[v][x][y]=(dp[v][x][y]+(LL)dp[u][x][y]*(f(x,y)-i))%MOD;
if(x) dp[v][x-1][y]=(dp[v][x-1][y]+(LL)dp[u][x][y]*(f(x-1,y)-f(x,y)))%MOD;
if(y) dp[v][x][y-1]=(dp[v][x][y-1]+(LL)dp[u][x][y]*(f(x,y-1)-f(x,y)))%MOD;
}
}
cout<<dp[n%2][0][0]<<endl;
return 0;
}