• UVA 11698 Code Permutations


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

    题目

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

    $1leqslant Nleqslant 100,1leqslant Kleqslant 2^{31}-1$

    题解

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

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

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

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

    $dp[i][j]=sum_{dmid 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_{dmid 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
    ",dp[n][(1<<fac.size())-1]);
    	}
    }
    
  • 相关阅读:
    PostgreSQL学习手册(五) 函数和操作符
    数据表的左连接与右连接
    ESX虚拟机文件列表详解
    程序员生存定律-打造属于自己的稀缺性
    C语言字符串处理函数
    托福听力
    DNS map file in windows
    Ubuntu 下安装VNC server
    PostgreSQL 数据库错误状态编号解释[附带列表
    [留学新生须知]新生美国生活常用单词汇总
  • 原文地址:https://www.cnblogs.com/sahdsg/p/12838161.html
Copyright © 2020-2023  润新知