时隔多日 我又来挑战这道dp。

几个月前给写自闭了。几个月后再来。

首先一个我们能列出来的状态 是以行为转移的 f[i]表示前i行...但是会发现此时列我们控制不了 且棋子的颜色,个数我们也要放到状态里。

这个dp是一个完成不了 或者说复杂度过高的dp。

必须得换一个状态 可以想到 由于每种颜色棋子独立 所以我们没有必要若干个颜色的棋子进行混合求方案。

可以一种一种颜色的棋子放。

所以有状态 f[i][j][k]表示前i种颜色的棋子占领了j行K列的方案数。

这样行和列的状态有了 我们只需要安排一下当前颜色怎么放。

当前颜色有a[i]个棋子。枚举一下 这么多棋子能占 l行 r列。

发现我们很难去形成用a[i]个棋子完美占领l行r列。但是若a[i]<l||a[i]<r方案数显然为0.

设S=空余的位置数 C(S,a[i])我们先强行安排位置。再减掉不合法的方案。

不合法方案=没有占领够l行+没有占领够r行-前两者的交集。

这里的容斥里面是一个类似于代表元容斥的东西。显然没够l行 说明最多<=l-1都是不合法的 对列同样如此。

最后两者不合法的交集要加回来即可。

不过这样容斥是错误的 原因是 我们的不合法方案中可能有状态是重复的如l-1行不合法我们外面还要乘一个C(l,l-1)*l-1行的不合法。

仔细观察 l-1行的不合法并不代表l-1行全部被占 所以外面再乘以l说明一些方案被重复计算了但是不这样做也不对 不合法的方案需要枚举出哪一行没有被占。

所以我们考虑一个新的容斥 这次我们不合法方案定义为 精确的占了wl,wr行 确保他们都被占了 这样就不会出现上述情况了。

但是wl,wr需要枚举 我们需要把所有不合法的都给减掉。这样是\(c\cdot n^3\cdot m^3\)发现这个东西显然可以预处理一下 所以复杂度降低一个nm.

这样总复杂度为\(sum \cdot n^2\cdot m^2\) 可以通过。我终于战胜了这道题目。

有的晕了。。

需要注意的是 开longlong (我以为一个地方不会爆然后爆了 千万不要以为!

对于预处理 我们只需要处理a[i]即可 我没想好 把1~mx都给处理了。

const int MAXN=31,maxn=2010;
int n,m,c,maxx,mx;
int a[MAXN],vis[maxn];
ll g[maxn][MAXN][MAXN];//表示i个棋子占了j行k列的方案数。
ll f[MAXN][MAXN][MAXN];//f[i][j][k]表示前i种颜色占了i行j列的方案数.
ll fac[maxn],inv[maxn],ans;
inline int ksm(ll b,int p)
{
ll cnt=1;
while(p)
{
if(p&1)cnt=cnt*b%mod;
b=b*b%mod;
p=p>>1;
}
return cnt;
}
inline void prepare()
{
fac[0]=1;
rep(1,maxx,i)fac[i]=fac[i-1]*i%mod;
inv[maxx]=ksm(fac[maxx],mod-2);
for(int i=maxx-1;i>=0;--i)inv[i]=inv[i+1]*(i+1)%mod;
}
inline ll C(int a,int b)
{
if(a<b)return 0;
return fac[a]*inv[b]%mod*inv[a-b]%mod;
}
int main()
{
//freopen("1.in","r",stdin);
get(n);get(m);get(c);
rep(1,c,i)get(a[i]),mx=max(mx,a[i]),vis[a[i]]=1;
maxx=n*m;prepare();
f[0][0][0]=1;g[0][0][0]=1;
rep(1,mx,i)
{
if(!vis[i])continue;
rep(1,n,j)rep(1,m,k)
{
if(i<j||i<k)continue;
rep(1,j,w1)rep(1,k,w2)
{
if(w1==j&&k==w2)g[i][j][k]=(g[i][j][k]+C(j*k,i))%mod;
else g[i][j][k]=(g[i][j][k]-C(j,w1)*C(k,w2)%mod*g[i][w1][w2]%mod)%mod;
}
}
}
rep(1,c,i)//前i种颜色
{
rep(0,n,j)rep(0,m,k)
{
if(!f[i-1][j][k])continue;
rep(1,n-j,l)rep(1,m-k,r)
{
if(a[i]<l||a[i]<r)continue;
f[i][j+l][k+r]=(f[i][j+l][k+r]+C(n-j,l)*C(m-k,r)%mod*f[i-1][j][k]%mod*g[a[i]][l][r]%mod)%mod;
}
}
}
rep(1,n,j)rep(1,m,k)ans=(ans+f[c][j][k])%mod;
printf("%lld\n",(ans+mod)%mod);
return 0;
}

最新文章

  1. Gradle配置APK自动签名完整流程
  2. Windows Azure Storage (17) Azure Storage读取访问地域冗余(Read Access – Geo Redundant Storage, RA-GRS)
  3. ASP.NET Web API自身对CORS的支持: CORS授权检验的实施
  4. 2016暑假多校联合---Joint Stacks (STL)
  5. 【mysql】高可用集群之MMM
  6. 【JDBC】预编译SQL与防注入式攻击
  7. Linux 查看文件修改
  8. server 2008 ftp 环境重点说明
  9. centos 用户切换
  10. Android学习笔记(十一)——从意图返回结果
  11. 1、API
  12. 四种生成和解析XML文档的方法详解
  13. WEB部分题目writeup
  14. 如何解决Dynamics 365的错误:用户身份验证无效,MSIS0006
  15. .NET Core 2.0 项目的智能提示是英文,改成中文的解决方案
  16. python简单爬虫 使用pandas解析表格,不规则表格
  17. python的无限循环及退出
  18. jenkins每次构建前删除工作目录操作
  19. 【C++】C++中assert和ENDEGU预处理语句
  20. 【面试题】java面试题整理(有空再贴答案)

热门文章

  1. sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
  2. 区间dp(低价回文)
  3. scala 数据结构(五):队列 Queue
  4. Python基础最难知识点:正则表达式(使用步骤)
  5. bzoj4631踩气球
  6. Controller怎么接收Ajax传来的data
  7. 小书MybatisPlus第4篇-表格分页与下拉分页查询
  8. git只操作某个文件夹
  9. (数据科学学习手札90)Python+Kepler.gl轻松制作时间轮播图
  10. 使用Java带你打造一款简单的外卖系统