码迷,mamicode.com
首页 > 其他好文 > 详细

UVA 11698 Code Permutations

时间:2020-05-06 19:27:05      阅读:58      评论:0      收藏:0      [点我收藏+]

标签:之间   网上   原来   std   using   ati   结果   单位   vector   

https://vjudge.net/problem/UVA-11698

题目

输入n,k,求秩为k的n元置换个数(即k是最小的正整数,使置换$A^k(x)=x$),结果对$2^{31}-1$取模

$1\leqslant N\leqslant 100,1\leqslant K\leqslant 2^{31}-1$

题解

= =很神奇的题目,网上也找不到题解,甚至在官方题解上(有错误)纠结了好久……不会做,最后还是看了别人的提交才过的

首先把置换分解为循环,于是问题变为:

把数字1~N分成一份或几份(份之间可以交换),每一份串成一个循环(可以旋转),所有循环大小的最小公倍数是K,问有多少种分法。

设dp[i][j]为长度为i,最小公倍数是j的分法,那么可以通过枚举最后1所在的循环的大小得到

$dp[i][j]=\sum_{d\mid K} A_{i-1}^{j-1}dp[i-d][?]$

边界为dp[0][1]=1,因为LCM(1,x)=x,可以看成单位元= =

 

可以发现?处可以取很多数……只要LCM(?,d)=j都可以取

其中$A_{i-1}^{j-1}$表示这个环的元素,因为1就在里面所以不管,循环按照顺时针拆成链,剩下的元素按照大小顺序对应到原来的1,2,3,…,i-d

那么就是枚举?和d,然后LCM计算出j

 

时间复杂度O(n^3)(枚举?、d、i,通过?和d计算j)

 

然后可以状态压缩一下,因为根据算术基本定理,最小公倍数是指数取最大值

那么只要指数没取到最大值就可以分成一类情况,只能一步登天,没有中间状态

设$dp[i][k]$为长度为$i$,k表示每一个指数是否取到最大值

那么可以写出

$dp[i][k|k_d]=\sum_{d\mid K} A_{i-1}^{j-1}dp[i-d][k]$

k的第i位表示第i个指数是否达到了题目要求的最大指数

根据乘法$2*3*5*7*11*13*17*19*23*29>2^{31}-1$,可以得到k最多有10位

边界为dp[0][0]=1

 

时间复杂度O(2^log(k) n^2)(枚举k、d和i)

AC代码

#include<cstdio>
#include<cmath>
#include<cstring>
#include<vector>
#define MO ((1ull<<31)-1)
using namespace std;
int A[107][107];
inline void db() {
	A[0][0]=1;
	for(int i=1; i<=100; i++) {
		A[i][0]=1;
		for(int j=1; j<=i; j++) {
			A[i][j] = 1ll*A[i-1][j-1]*i%MO;
		}
	}
}
unsigned dp[107][1027];
vector<int> fac, dd, dk;
int main() {
	db();
	memset(dp,0,sizeof dp);
	int T; scanf("%d", &T);
	while(0<T--) {
		int n,k; scanf("%d%d", &n, &k);
		int kk=k;
		fac.clear(); dd.clear(); dk.clear();
		for(int i=2; i<=n; i++) if(kk%i==0) {
			int now=1;
			do {kk/=i; now*=i;} while(kk%i==0);
			fac.push_back(now);
		}
		if(kk>1) {puts("0"); continue;}
		
		for(int i=1; i<=n; i++) if(k%i==0) {
			int k=0;
			for(int j=0; j<(int)fac.size(); j++) if(i%fac[j]==0) {
				k|=1<<j;
			}
			dk.push_back(k);
			dd.push_back(i);
		}
		memset(dp,0,sizeof dp);
		dp[0][0]=1;
		for(int i=1; i<=n; i++) {
			for(int j=0; j<(int)dd.size(); j++) {
				for(int k=0; k<(1<<fac.size()); k++) if(i>=dd[j]) {
					dp[i][k|dk[j]] = (dp[i][k|dk[j]]+1ll*dp[i-dd[j]][k]*A[i-1][dd[j]-1])%MO;
				}
			}
		}
		printf("%d\n",dp[n][(1<<fac.size())-1]);
	}
}

 

UVA 11698 Code Permutations

标签:之间   网上   原来   std   using   ati   结果   单位   vector   

原文地址:https://www.cnblogs.com/sahdsg/p/12838161.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!