当前位置: 代码迷 >> 综合 >> 【容斥原理】(AtCoder Regular Contest 093 F) Dark Horse
  详细解决方案

【容斥原理】(AtCoder Regular Contest 093 F) Dark Horse

热度:38   发布时间:2023-09-27 07:48:06.0

题意:

2N2N个选手参与一场比赛,比赛规则是:相邻的两个人比赛一次,败者淘汰掉,胜者继续进行,直到只剩一个人为止。
现在给出1号选手会败给哪些选手(实力摸得很清楚啊)
并且已知其他选手之间均满足:两个选手比赛,编号小的一定会胜利。
现在可以安排每个选手初始的位置,要 钦定 1号选手 Chicken Dinner 最后获胜,求能满足条件的初始位置的方案数。


分析:

首先,如果1号选手要胜利到最后,那么它一定会打N场比赛。
我们可以用一个图表来表示它的比赛过程:
【容斥原理】(AtCoder Regular Contest 093 F) Dark Horse
可以看出,它首先与1个人比赛,再与两个人的编号最小的比赛,再与4个人的编号最小的比赛。并且,我们发现,无论他在哪个位置,都一定是按照这样的顺序(先与1个人,再与两人最小值……)
所以,我们可以令其在1号位置。得到方案后,再乘上2N2N即可

现在,我们可以将与其比赛的人的区间再简化一点:
GiGi表示站i号位置的选手
那么G1G1一定会与
G2,min{ G3,G4},min{ G5,G6,G7,G8}min{ G2N?1+1,G2N?1+2,,G2N}G2,min{G3,G4},min{G5,G6,G7,G8}……min{G2N?1+1,G2N?1+2,……,G2N}比赛

这样一来,我们只需要使得每一个块内的最小值都不在A的范围内即可。

接下来就是本题最有趣的地方了:容斥原理。
设S为N个块的一个子集,f(S)f(S)表示这个子集中所有块的最小值均在A的范围内(其余块是否在A的范围内不考虑)的方案数。

这样一来,最终答案即为(?1)|S|f(S)∑(?1)|S|f(S)

现在证明一下:
首先,根据定义:f(0)f(0)代表了全部情况,
对任意一个我们要减去的方案来说(即至少存在一个块的最小值在A范围内),设其为S1S1
对任意一个S?S1S′?S1f(S)f(S′)都会将S1S1这个方案计算进去,我们加上其最终权值,就能得到S1S1最终的贡献。
那么其最终的贡献为:
?C1|S|+C2|S|?C3|S|C|S||S|=?1?C|S|1+C|S|2?C|S|3……C|S||S|=?1
如果你这个都证明不来。。那我只能推荐你多学学数论了
我还是写写吧:

C0n+C2n+C4n+Cn0+Cn2+Cn4+……

=C0n?1+C1n?1+C2n?1++Cn?1n?1=Cn?10+Cn?11+Cn?12+……+Cn?1n?1

=C1n+C3n+C5n+=Cn1+Cn3+Cn5+……

所以
C0n+C2n+C4n+?C1n?C3n?C5n?=0Cn0+Cn2+Cn4+……?Cn1?Cn3?Cn5?……=0

所以
?C0n=?C1n+C2n?C3n=?1?Cn0=?Cn1+Cn2?Cn3……=?1

是不是觉得容斥原理很妙?
再来看f(S)f(S)怎么求:由于其余部分不考虑,我们只需要考虑哪些块的最小值必须在A中即可。

那么我们将A从大到小排序,依次考虑加入AiAi后的f(S)f(S)
1、成为一个块的最小值:那么在这个块中,必须填充相应数量的,编号比AiAi大的选手,组合数统计。
2、不成为块的最小值,不操作即可dp[i][S]+=dp[i?1][S](dp[i][S]+=dp[i?1][S])

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 65636
#define MAXM 20
#define MOD 1000000007
using namespace std;
long long dp[2][MAXN],q[MAXN],p[MAXN];
int a[MAXM],n,m;
bool cmp(int x,int y){return x>y;
}
long long fsp(long long x,int y){long long res=1;while(y){if(y&1)res=(res*x)%MOD;x=(x*x)%MOD;y>>=1;}return res;
}
long long C(int x,int y){if(x<y)return 0;return ((q[x]*p[y])%MOD*p[x-y])%MOD;
}
int count(int x){int res=0;while(x){if(x&1)res++;x>>=1;}return res%2;
}
int main(){SF("%d%d",&n,&m);for(int i=1;i<=m;i++)SF("%d",&a[i]);q[0]=1;for(int i=1;i<(1<<n);i++)q[i]=(1ll*q[i-1]*i)%MOD;p[(1<<n)-1]=fsp(q[(1<<n)-1],MOD-2);for(int i=(1<<n)-1;i>=1;i--)p[i-1]=(1ll*p[i]*i)%MOD;sort(a+1,a+1+m,cmp);int now=0;dp[now][0]=1;for(int i=1;i<=m;i++){now^=1;for(int j=0;j<(1<<n);j++){if(dp[now^1][j]==0)continue;dp[now][j]=(dp[now][j]+dp[now^1][j])%MOD;int les=(1<<n)-1-j;for(int k=0;k<n;k++)if((j&(1<<k))==0){dp[now][j|(1<<k)]+=((C(les-a[i]+1,(1<<k)-1)*dp[now^1][j])%MOD*q[1<<k])%MOD;dp[now][j|(1<<k)]%=MOD;}}memset(dp[now^1],0,sizeof dp[now^1]);}long long ans=0;for(int i=0;i<(1<<n);i++){if(dp[now][i]==0)continue;dp[now][i]=(dp[now][i]*q[(1<<n)-1-i])%MOD;if(count(i)==0)ans+=dp[now][i];elseans-=dp[now][i];ans%=MOD;}ans<<=n;ans%=MOD;PF("%lld",(ans+MOD)%MOD);
} 
  相关解决方案