Codeforces 232 B Table 题解 [ 蓝 ] [ 分组背包 ] [ 组合数学 ] [ 循环节 ]
蒟蒻模拟赛上场切的一道蓝,非常难以置信我竟然能做蓝题。
这题的数据范围初看还是比较坑的,
思路
首先观察到每个边长为
可以观察到:左边矩形与右边矩形重合的部分为中间灰色部分,它包含的点数记为
那么由题目条件可知:
因此可得:
所以我们可以发现,正方形每往后移动一位,移动前的第一列和移动后的最后一列的点数是一样的。
而本题求的是方案数,对于有
如果我们把正方形每次的移动看做把第一列移动到最后一列接上,那么我们可以发现,正方形各列的点数形成了循环节。
于是对于正方形的每一列,我们把它看做一个类型,第
接下来的问题就是求把
这是个很显然的分组背包,我们把每一列看做一个组,假设这一列在棋盘中出现了
正确时间复杂度为
注意优化常数,如果在转移过程中再计算快速幂和组合数那么会导致复杂度变成
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll n,m,k,ans=0,f[50005],g[50005],dp[50005],p[105][2];
ll qp(ll x,ll y)
{
ll res=1;
while(y)
{
if(y&1)res=res*x%mod;
y>>=1;
x=x*x%mod;
}
return res%mod;
}
ll niyuan(ll x)
{
return qp(x,mod-2);
}
void init()
{
f[0]=1;
g[0]=1;
for(int i=1;i<=10000;i++)
{
f[i]=f[i-1]*i%mod;
g[i]=g[i-1]*niyuan(i)%mod;
}
}
ll c(ll m,ll n)
{
return 1ll*f[m]*g[m-n]%mod*g[n]%mod;
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
init();
cin>>n>>m>>k;
dp[0]=1;
for(ll i=0;i<=n;i++)
{
p[i][0]=qp(c(n,i),m/n);
p[i][1]=qp(c(n,i),m/n+1);
}
for(int i=1;i<=n;i++)
{
for(int j=k;j>=0;j--)
{
int lmt=min(1ll*j,n);
for(int l=1;l<=lmt;l++)
{
ll tmp;
if((m%n)>=i)tmp=p[l][1];
else tmp=p[l][0];
dp[j]=(dp[j]+dp[j-l]*tmp%mod)%mod;
}
}
}
cout<<dp[k];
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战