zoukankan      html  css  js  c++  java
  • 【题解】 SRM686 CyclesNumber 置换+斯特林数 TOC14199

    Legend

    Link ( extrm{to TopCoder})

    求所有长度为 (n) 的置换的循环个数的 (m) 次方之和。

    (n le 10^5,m le 500)

    Editorial

    (x_i) 表示 (i) 这个循环是否存在,答案就是:

    [sum_{p ext{ is permutation}} left(sum_{} x_i ight)^m ]

    根据

    [left(sum_{i=1}^{n} x_i ight)^m= sum_{j=0}^{m} egin{Bmatrix} m \ jend{Bmatrix}j! dbinom{sum x_i}{j} ]

    我们知道如果钦定 (k) 个循环出现,那么就会贡献 (egin{Bmatrix} m \ kend{Bmatrix} k!)。显然循环的数量太多了,没有办法枚举,怎么办?

    非常巧妙的转化:选出了 (k) 个循环后,新增一个垃圾节点 (n+1),把剩余元素和 (n+1) 分在一起做一个循环,就可以映射出剩余的所有循环方案。即最后答案就是:

    [sum_{k=0}^{n} egin{bmatrix} n + 1 \k + 1end{bmatrix} egin{Bmatrix} m \ kend{Bmatrix} k! ]

    这一步转化非常的奇怪,乍一想挺让人无法理解的,但实际上可以这么考虑:把每一个循环从小到大展开,把循环内的最大值移动到每个循环的第一个位置。再按每一个循环的最大值对循环排序,把 (n+1) 放到整个序列开头。不难发现这样就构造出了置换与圆排列的双射关系。

    其实这个想法与 [FJOI2016]建筑师 有些类似。

    (n) 这么大我们是不是要写多项式呀?

    然而 (n) 很大的时候后面的第二类斯特林数的值是 (0),不用求。

    复杂度 (O(nm+m^2))

    Code

    #include <bits/stdc++.h>
    
    #define LL long long
    
    const LL MOD = 1e9 + 7;
    
    LL s[100000 + 23][302] ,S[302][302] ,fac[302];
    
    void init(){
    	s[0][0] = 1;
    	for(int i = 1 ; i <= 100001 ; ++i){
    		for(int j = 1 ; j <= 301 ; ++j){
    			s[i][j] = (s[i - 1][j] * (i - 1) + s[i - 1][j - 1]) % MOD;
    		}
    	}
    	S[0][0] = 1;
    	for(int i = 1 ; i <= 301 ; ++i){
    		for(int j = 1 ; j <= 301 ; ++j){
    			S[i][j] = (S[i - 1][j] * j + S[i - 1][j - 1]) % MOD;
    		}
    	}
    	fac[0] = 1;
    	for(int i = 1 ; i <= 301 ; ++i) fac[i] = fac[i - 1] * i % MOD;
    }
    
    class CyclesNumber{
    	private:
    	
    		LL solve(int n ,int m){
    			LL Ans = 0;
    			for(int i = 0 ; i <= std::min(n ,m) ; ++i){
    				Ans = (Ans + s[n + 1][i + 1] * S[m][i] % MOD * fac[i]) % MOD;
    			}
    			return Ans;
    		}
    	public:
    		CyclesNumber(){
    			init();
    		}
    		std::vector<int> getExpectation(std::vector<int> n ,std::vector<int> m){
    			std::vector<int> Ans;
    			for(int i = 0 ; i < n.size() ; ++i){
    				Ans.push_back(solve(n[i] ,m[i]));
    			}
    			return Ans;
    		}
    }F;
    
    void output(std::vector<int> a){
    	for(auto i : a){
    		printf("%d
    " ,i);
    	}
    }
    
    int main(){
    	output(F.getExpectation({10 ,20 ,30} ,{10 ,20 ,30}));
    }
    
  • 相关阅读:
    Java基础学习总结(41)——JPA常用注解
    Java基础学习总结(41)——JPA常用注解
    【云速建站】视频播放专题
    一招教你如何修复MySQL slave中继日志损坏问题
    【nodejs原理&源码赏析(3)】欣赏手术级的原型链加工艺术
    【云速建站】后台数据批量导入导出
    【云速建站】会员注册弹窗添加及设置
    【nodejs原理&源码赏析(2)】KOA中间件的基本运作原理
    【nodejs原理&源码赏析(1)】Express中间件系统的基本实现
    补习系列(5)-springboot- restful应用
  • 原文地址:https://www.cnblogs.com/imakf/p/13801335.html
Copyright © 2011-2022 走看看