P3977 [TJOI2015]棋盘 题解
前制知识引导
状态压缩动态规划:P1896 [SCOI2005] 互不侵犯
矩阵加速优化:P1962 斐波那契数列
1.抓住数据范围的特点列出 dp 方程。
由于
用一个
2.找到合法方案
我们先得找到能使本行合理的二进制数。
例如一个棋子本行攻击模板为
那么有一个在第
那么等价于不能有两个
所以当二进制数
由于棋子在模板中的第一行,所以处理不同行的两个二进制数要分从上往下和从下往上。
考虑暴力枚举。每遇到一个下方二进制数
当遇到下方的
总复杂度
3.矩阵快速幂优化
上面复杂度明显不能过掉本题,所以考虑优化。
对于每一次 dp 转移,每一个
那么就可以考虑矩阵优化。
建立一个
对于初始矩阵的每一位
对于转移矩阵的每一位
最后对于初始矩阵进行
时间复杂度为
4.细节处理
由于模数为
但毕竟已经允许用 __int128 了,所以就偷个懒吧。
#include<iostream>
#include<cstdio>
#define int __int128
using namespace std;
const int M=6;
int n,m,p,k,mod=1,t[5][10],vis[1<<M];
inline int read()
{
char ch=getchar();
int f=1,sum=0;
while(ch>'9'||ch<'0')
{
if(ch=='-')f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
sum=(sum<<1)+(sum<<3)+(ch-48);
ch=getchar();
}
return sum*f;
}
inline void write(int x)
{
if(x<0)
{
putchar('-');
x=-x;
}
if(x/10)write(x/10);
putchar(x%10+'0');
}
void take_spc()
{
for(int i=0;i<1<<m;i++)
{
vis[i]=1;
for(int j=1;j<=p;j++)
{
if(t[2][j])
{
if(j<k&&(i&(i<<(k-j))))vis[i]=0;
if(j>k&&(i&(i>>(j-k))))vis[i]=0;
}
}
}
}
struct matrix
{
int a[1<<M+1][1<<M+1],x,y;
void init()
{
for(int i=1;i<=x;i++)
{
for(int j=1;j<=y;j++)
{
a[i][j]=0;
}
}
}
}u,v;
matrix operator *(matrix fi,matrix se)
{
matrix th;
th.x=fi.x,th.y=se.y;
th.init();
for(int i=1;i<=fi.x;i++)
{
for(int j=1;j<=se.y;j++)
{
for(int p=1;p<=fi.y;p++)
{
th.a[i][j]+=fi.a[i][p]*se.a[p][j]%mod;
th.a[i][j]%=mod;
}
}
}
return th;
}
bool check(int fi,int se)
{
if(!vis[fi]||!vis[se])return false;
for(int i=0;i<m;i++)
{
if(fi&(1<<i))
{
for(int j=i-k+2,pt=1;j<=i+p-k+1;j++,pt++)
{
if(j<1||j>m)continue;
if(t[3][pt]&&((1<<(j-1))&se))return false;
}
}
}
for(int i=0;i<m;i++)
{
if(se&(1<<i))
{
for(int j=i-k+2,pt=1;j<=i+p-k+1;j++,pt++)
{
if(j<1||j>m)continue;
if(t[1][pt]&&((1<<(j-1))&fi))return false;
}
}
}
return true;
}
void quick_pow(int num)
{
while(num)
{
if(num&1)v=u*v;
u=u*u;
num>>=1;
}
}
signed main()
{
for(int i=1;i<=32;i++)mod*=2;
n=read(),m=read(),p=read(),k=read();
k++;
for(int i=1;i<=3;i++)for(int j=1;j<=p;j++)t[i][j]=read();
take_spc();
u.x=1<<m,u.y=1<<m,u.init();
v.x=1<<m,v.y=1,v.init();
u.x=1<<m,v.y=1;
for(int i=0;i<1<<m;i++)if(vis[i])v.a[i+1][1]=1;
for(int i=0;i<1<<m;i++)
{
for(int j=0;j<1<<m;j++)
{
if(check(i,j))u.a[i+1][j+1]=1;
}
}
quick_pow(n-1);
int ans=0;
for(int i=1;i<=1<<m;i++)ans+=v.a[i][1],ans%=mod;
write(ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)