再度死在了这种排列组合题的大刀下
一个显然的结论:一套UNO牌在复制条件下可以打完的条件当且仅当同点数同颜色的牌连边后形成连通块
这道题只有4种颜色,所以会有两种做法
1.列出DP表达式后矩阵快速幂转移
2.发现每张牌的颜色状态只有
4
4
4种
对于一个相同点数,我们枚举这个点数下所有牌颜色的并
颜色并的可能种数只有
2
4
2^4
24种,可以状压
问题转化成有n个不同点数,m种颜色并的可能,n个点数要分别匹配一个颜色并,每种颜色并不能为空的方案数
化成小球-盒子模型其实就是第二类斯特林数
容斥公式为
∑
i
=
0
m
(
−
1
)
m
−
i
i
n
C
(
i
,
m
)
\sum_{i=0}^{m}(-1)^{m-i}i^nC(i,m)
∑i=0m(−1)m−iinC(i,m)
然后就OK了
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define lowbit(x) x&(-x)
using namespace std;
ll i,j,k,m,n,o,p,l,s,t,ans,opt,mo;
ll fac[17],b[17],c[5],fa[5],vis[5],d[5];
int a[16][4]={{0,0,0,0},{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1},{1,1,0,0},{1,0,1,0},{1,0,0,1},{0,1,1,0},{0,1,0,1},{0,0,1,1},{1,1,1,0},{1,1,0,1},{1,0,1,1},{0,1,1,1},{1,1,1,1}};
ll ksm(ll x,ll y)
{
ll ans=1;
for (;y;y>>=1,x=x*x%mo)
if (y&1) ans=ans*x%mo;
return ans;
}
ll C(ll x,ll y) {return (fac[y]/fac[x]/fac[y-x])%mo;}
ll getfather(ll x) {return fa[x]==x?x:fa[x]=getfather(fa[x]);}
void add(ll x,ll y)
{
int xx=getfather(x),yy=getfather(y);
fa[yy]=xx;
}
int main()
{
freopen("uno.in","r",stdin);
freopen("uno.out","w",stdout);
scanf("%lld%lld",&n,&mo);
for (i=fac[0]=1;i<=16;i++) fac[i]=fac[i-1]*i;
for (i=1;i<=(1<<16)-1;i++)
{
for (o=i,m=0;o;o-=lowbit(o)) m++;
for (j=1,b[0]=0;j<=15;j++)
if ((1<<(j-1))&i) b[++b[0]]=j;
memset(c,0,sizeof(c));
for (j=0;j<=5;j++) fa[j]=j,vis[j]=0;
d[0]=0;
if (i==8193)
{
int sg=0;
}
for (j=1;j<=b[0];j++)
{
c[0]=0;
for (k=0;k<=3;k++)
{
if (a[b[j]][k])
{
c[++c[0]]=k+1;
if (!vis[k]) vis[k]=1,d[++d[0]]=k+1;
}
}
for (k=1;k<=c[0]-1;k++) add(c[k],c[k+1]);
}
t=0;
for (j=1;j<=d[0];j++)
if (getfather(d[j])!=getfather(d[1]))
{
t=1;break;
}
if (t) continue;
if (i==5)
{
int sdfsg=0;
}s=0;
for (j=0;j<=m;j++)
{
opt=(m-j)%2?-1:1;
s=(s+ksm(j,n)*C(j,m)%mo*opt%mo),s%=mo;
}
ans=(ans+s)%mo;
}
printf("%lld\n",(ans+mo)%mo);
return 0;
}