• 【AT3981】[ARC093D] Dark Horse(容斥+状压DP)


    点此看题面

    • (2^n)个人按照二叉树结构进行淘汰赛。
    • 给出(m)个人(a_{1sim m}),你只会输给他们,碰上别人都能胜利,而其余人比赛时编号小的人胜。
    • 求有多少种情况你能获胜。
    • (n,mle16)

    经典容斥

    首先,显然无论你处在什么位置,获胜的方案数都是一样的。

    因此,不妨假定你处在第一个位置,那么只要最终给答案乘上一个(2^n)即可。

    对于这道题直接计算能获胜的情况数其实并不太容易,因此套路地想到容斥。

    所以就只需考虑你在一路向上的过程中至少会遇见哪些能战胜你的人。

    状压(DP)

    我们设(f_{i,j})表示枚举到了第(i)个人,你一路向上的(n)个位置中已经被占据的位置的状压结果为(j)

    这里需要从后往前枚举(a_i),这样一来的一大好处就是能被当前(a_i)战胜的人肯定包含了之前就能被战胜的人,方便后续的转移。

    那么一种最简单的转移就是不考虑这个人,直接从(f_{i+1,j})转移向(f_{i,j})

    否则,去枚举一个未被占据的位置(k),相当于(a_i)要从(2^k)个人中脱颖而出来战胜你。

    考虑已经用过的人数应该是(j),所以剩下的能被(a_i)战胜的人数是(2^n-a_i-j),而需要选出的人数应该是(2^k-1)

    因此转移就是从(f_{i+1,j})转移向(f_{i,j|2^k}),乘上一个(C_{2^n-a_i-j}^{2^k-1} imes (2^k)!)的系数,其中的阶乘表示这(2^k)个人可以按任意顺序。

    最后容斥的结果就应该是:(其中的阶乘表示没被考虑到的人可以按任意顺序)

    [sum_{i=0}^{2^n-1}(-1)^{g_i} imes(f_{1,i} imes(2^n-1-i)!) ]

    代码:(O(nm2^n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 16
    #define X 1000000007
    #define C(x,y) (0<=(y)&&(y)<=(x)?1LL*Fac[x]*IFac[y]%X*IFac[(x)-(y)]%X:0)
    using namespace std;
    int n,m,a[N+5],f[N+5][1<<N],g[1<<N];
    I int QP(RI x,RI y) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    int Fac[1<<N],IFac[1<<N];I void InitFac(CI S)//预处理阶乘和阶乘逆元
    {
    	RI i;for(Fac[0]=i=1;i<=S;++i) Fac[i]=1LL*Fac[i-1]*i%X;
    	for(IFac[i=S]=QP(Fac[S],X-2);i;--i) IFac[i-1]=1LL*IFac[i]*i%X;
    }
    int main()
    {
    	RI i;for(scanf("%d%d",&n,&m),i=1;i<=m;++i) scanf("%d",a+i);
    	RI l=1<<n;for(InitFac(l),i=1;i^l;++i) g[i]=g[i>>1]+(i&1);
    	RI j,k;for(f[m+1][0]=1,i=m;i;--i) for(j=0;j^l;++j)//枚举状态
    		if(f[i+1][j]) for(f[i][j]=(f[i][j]+f[i+1][j])%X,k=0;k^n;++k)//枚举一个未被占据的位置
    			!(j>>k&1)&&(f[i][j|1<<k]=(1LL*C(l-a[i]-j,(1<<k)-1)*Fac[1<<k]%X*f[i+1][j]+f[i][j|1<<k])%X);//转移
    	RI t=0;for(i=0;i^l;++i) t=(1LL*(g[i]&1?X-1LL:1LL)*Fac[l-1-i]%X*f[1][i]+t)%X;return printf("%d
    ",1LL*t*l%X),0;//容斥
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    349. 两个数组的交集
    383. 赎金信
    242. 有效的字母异位词
    844. 比较含退格的字符串
    904. 水果成篮
    剑指offer(9)变态跳台阶
    剑指offer(8)跳台阶
    剑指offer(7)斐波那契数列
    剑指offer(6)旋转数组的最小数字
    剑指offer(5)用两个栈实现队列
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT3981.html
Copyright © 2020-2023  润新知