• ARC93F Dark Horse


    Dark Horse

    (2^n) 个人打锦标赛,他们的过程是随机一个排列,然后按照这个排列站好。每轮是第 (2i − 1) 个人和第 (2i) 的人比赛,败者淘汰。

    你是 (1) 号选手,你碰到 (a_1, a_2,dots, a_m) 会输,碰到剩下的会赢。如果比赛和你无关,那么编号小的赢。

    求有多少个排列,能够使你最后赢。答案对 (10^9 + 7) 取模。

    (1 ≤ n ≤ 16, 0 ≤ m ≤ 16, 2 ≤ a_i ≤ 2^n)

    题解

    对做题的人而言,分析法比解析法更有逻辑。

    首先可以发现这棵二叉树每个非叶子节点都可以交换左右儿子。所以我们可以钦定 (1) 号选手站在 (1) 号位置来找本质不同的方案。

    既然要 (1) 号胜出,那么我们就来看看 (1) 号最终会跟哪些人比赛。显然有 ({2},{3,4},{5,6,7,8}dots) 这些位置上的最小值。写成通项:

    [P_k={p_{2^{k-1}+1},dots,p_{2^k}},kin[1,n] ]

    那么我们要求 (forall kin [1,n],min P_k otin A)

    “最小值不属于 (A)”的方案太多,但是“最小值属于 (A)”却给了一个最小值是某个 (a_i) 的限制。所以这个反面更可做。

    考虑容斥。

    [frac{ans}{2^n}=(2^n-1)!+sum_{Ssubseteq {P}}(-1)^{|S|}F(S) ]

    其中 (F(S)) 表示 (S) 中的 (P) 均满足 (min Pin A),而非 (S) 中的 (P) 无所谓的方案数。非 (S) 中的 (P) 的方案数可以在 (S) 中的 (P) 确定后用排列数搞定,所以进一步的,

    [F(S)=(2^n-1-sum_{Pin S}|P|)!G(S) ]

    其中 (G(S)) 表示给 (S) 中的 (P) 分配选手使得他们满足条件的方案数。

    显然若 (a_i<a_j),那么最小值钦定为 (a_j)(P) 能够选的其他球包含于最小值钦定为 (a_i)(P) 能选的球。按 (A) 中元素从大到小DP,记 (G(i,S)) 表示做到 (a_i) 被钦定的集合是 (S) 时的方案数,对于每一个 (a_i),我们做如下两个决策之一:

    1. 不把 (a_i) 作为某一集合钦定的最小值。

    2. 钦定 (a) 作为 (P_k) 的最小值,并再选取大于 (a) 且未被选取的 (2^{k-1}-1) 个数。

    最终答案为

    [ans=2^nleft((2^n-1)!+sum_{Ssubseteq {P}} (-1)^{|S|} imes (2^n-sum_{Pin S}|P|)! imes G(1,S) ight) ]

    时间复杂度 (O(2^nnm))

    CO int N=20,S=65536+10;
    int a[N];
    int fac[S],ifac[S],dp[N][S];
    
    IN int binom(int n,int m){
    	return mul(fac[n],mul(ifac[m],ifac[n-m]));
    }
    int main(){
    	int n=read<int>(),m=read<int>();
    	for(int i=m;i>=1;--i) read(a[i]);
    	int all=1<<n;
    	fac[0]=1;
    	for(int i=1;i<=all;++i) fac[i]=mul(fac[i-1],i);
    	ifac[all]=fpow(fac[all],mod-2);
    	for(int i=all-1;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
    	dp[0][0]=1;
    	for(int i=1;i<=m;++i)for(int s=0;s<all;++s){
    		int left=all-a[i]+1-s;
    		cadd(dp[i][s],dp[i-1][s]);
    		for(int j=0;j<n;++j)if(~s>>j&1){
    			if(left<1<<j) break;
    			cadd(dp[i][s|1<<j],mul(dp[i-1][s],
    				 mul(binom(left-1,(1<<j)-1),fac[1<<j])));
    		}
    	}
    	int ans=fac[all-1];
    	for(int s=1;s<all;++s){
    		int sum=mul(dp[m][s],fac[all-1-s]);
    		cadd(ans,__builtin_popcount(s)&1?mod-sum:sum);
    	}
    	cmul(ans,all);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    电影经典台词整理
    墨菲定律
    自我修养
    eclipse 点击 new window 后,关闭新增的窗口
    mysql 修改表结构以支持事务操作
    JavaScript高级程序设计学习笔记第六章--面向对象程序设计
    JavaScript高级程序设计学习笔记第五章--引用类型(函数部分)
    JavaScript高级程序设计学习笔记第五章--引用类型
    JavaScript高级程序设计学习笔记第四章--变量、作用域和内存问题
    JavaScript高级程序设计学习笔记第三章--基本概念
  • 原文地址:https://www.cnblogs.com/autoint/p/12149332.html
Copyright © 2020-2023  润新知