专栏文章
题解:[ARC093F] Dark Horse
AT_arc093_d题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mir2q6v4
- 此快照首次捕获于
- 2025/12/04 14:47 3 个月前
- 此快照最后确认于
- 2025/12/04 14:47 3 个月前
AT_arc093_d
题目描述
给定 , ,问在有 个编号为 的选手不可战胜的情况下,在一个 的满二叉树上进行淘汰制比赛,获胜的情况数
具体解释,你是 号选手,你无法战胜的选手有且仅有那 个,对于剩下的情况(你不参与的情况),编号小的人胜利,选手编号 。
题目分析
由于不管 在哪个位置,一轮轮下来,基本上过程都是相似的,所以假设 在第 个位置。
令集合 表示无法战胜的选手,集合 表示所有选手
它所遇见对手的情况无非是
把这些人分别用 表示,因此我们要求的就是:使得任意 且 ,它们的情况数目
发现 ,较小 ,所以想到状压 , 用 表示 , 中的 表示该位置 ,定义 表示集合 中的 值均出现在 中前 个数的方案数。
你问我不是要的是 中元素不相同吗
如果真的要这样就太复杂了
那我们反过来 ,考虑容斥 ,定义函数 表示 在二进制下 数量的奇偶性 , 奇返回 , 偶返回 , 令 上界为 , 可以证明最终结果
现在来考虑 的转移 :
- 不选第 个数 ,
- 选了第 个数 , 此时我们要去找 中有 的位置 , 把它填成 。 对应 ,不难分析 , 应该是由 个数字取 得到 , 一个位置填 , 剩下 个位置都应该取比 大且没有被选的数 ( 规则是这样说的 ) ,故有以下转移 :
for(int j=0;j<n;j++)
{
if(!((S>>j)&1))
{
les=(1<<n)-a[i]-S;
dp[i+1][S|(1<<j)]+=dp[i][j]*C(les,(1<<j)-1));
}
}
其中 表示从 个数中选 个数的方案数 ,不用多说
代表剩下可以选的数 , 表示总数 ,上面分析 性质可以推出 , 既可以表示状态也可以表示已经选了多少个数
为了方便计数 , 将 从大到小排序
最后,由于转移后没有考虑到 中剩下 中的选择情况 , 还要乘上 (也是由此推出容斥原理)
时间复杂度
CPP
#include<bits/stdc++.h>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
const int N=20,K=(1<<16)+7,p=1e9+7;
int n,m;
int a[N];
ll dp[N][K],ans;
ll fac[K],inv[K];
ll qpow(ll x,int n,ll res=1)
{
while(n)
{
if(n&1) res=res*x%p;
x=x*x%p;
n>>=1;
}
return res;
}
bool cmp(int a,int b)
{
return a>b;
}
void init(int n)
{
fac[0]=inv[0]=1;
for(int i=1;i<=n;i++) fac[i]=fac[i-1]*i%p;
inv[n]=qpow(fac[n],p-2);
for(int i=n-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%p;
}
ll C(int n,int m)
{
if(n<m) return 0;
return fac[n]*inv[m]%p*inv[n-m]%p;
}
int get_cnt(int x,int ans=0)
{
while(x)
{
ans++;
x-=lowbit(x);
}
return ans;
}
int main()
{
scanf("%d%d",&n,&m);init(1<<n);
for(int i=1;i<=m;i++) scanf("%d",&a[i]);
sort(a+1,a+m+1,cmp);
dp[0][0]=1;
for(int i=0;i<m;i++)
{
for(int j=0;j<(1<<n);j++)
{
int t=(1<<n)-a[i+1]-j;
dp[i+1][j]=(dp[i+1][j]+dp[i][j])%p;
for(int k=0;k<n;k++)
{
if(!((j>>k)&1))
{
dp[i+1][j|(1<<k)]=(dp[i+1][j|(1<<k)]+dp[i][j]*C(t,(1<<k)-1)%p*fac[1<<k]%p)%p;
}
}
}
}
int S=(1<<n)-1;
for(int i=0;i<=S;i++)
{
int cnt=get_cnt(i);
if(cnt&1) ans=(ans-dp[m][i]*fac[S-i]%p+p)%p;
else ans=(ans+dp[m][i]*fac[S-i]%p)%p;
}
ans=ans*(S+1)%p;
printf("%d",ans);
return 0;
}
是不是发现最后 乘上了一个 ,因为 可以出现在 个位置
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...