首页 > 其他分享 >P3977 [TJOI2015]棋盘 题解

P3977 [TJOI2015]棋盘 题解

时间:2023-02-24 13:23:33浏览次数:30  
标签:ch TJOI2015 P3977 int 题解 矩阵 th fi se

前制知识引导

状态压缩动态规划:[P1896 [SCOI2005] 互不侵犯](https://www.luogu.com.cn/problem/P1896)

矩阵加速优化:[P1962 斐波那契数列](https://www.luogu.com.cn/problem/P1962)

---
### 1.抓住数据范围的特点列出 dp 方程。

由于 $m\le6$,很容易想到利用状态压缩解决问题。

用一个 $6$ 位的二进制数存储每一行的状态,很容易能列出状态转移方程。

$\mathit{dp}_{i,j}=\sum_{k=1}^{(1<<m)-1} (\mathit{dp}_{i-1,k})$,其中 $k$ 与 $j$ 不能互相攻击。

-----
### 2.找到合法方案

我们先得找到能使本行合理的二进制数。

例如一个棋子本行攻击模板为 $1011$,$k=2$。

那么有一个在第 $0$ 列,距离 $k$ 差两个单位距离。

那么等价于不能有两个 $1$ 相隔距离为 $2$。

所以当二进制数 $j$ 满足 $(j\land(j<<2))=0$ 时合法。

由于棋子在模板中的第一行,所以处理不同行的两个二进制数要分从上往下和从下往上。

考虑暴力枚举。每遇到一个下方二进制数 $j$ 的 $1$ 时,在上方进行枚举,
当遇到下方的 $1$ 能攻击上方的 $1$ 时,方案不合法。从上往下同理。

总复杂度 $O(n \times 2^{m^2} )$。

-----

### 3.矩阵快速幂优化

上面复杂度明显不能过掉本题,所以考虑优化。

对于每一次 dp 转移,每一个 $j$ 都对应着固定的多个 $k$,并且每次转移重复做着求和工作。

那么就可以考虑矩阵优化。

建立一个 $m^2\times 1$ 的初始矩阵与一个 $m^2\times m^2$ 的转移矩阵。

对于初始矩阵的每一位 $(j,1)$,如果 $j$ 符合本行方案,那么赋值为 $1$,否则赋为 $0$。

对于转移矩阵的每一位 $(j,k)$,若 $j$ 与 $k$ 互不侵犯,则赋为 $1$,否则赋为 $0$。

最后对于初始矩阵进行 $(n-1)$ 的重复求积,可以利用快速幂解决。

时间复杂度为 $O(2^{m^3}\times \log(n))$。

---
### 4.细节处理

由于模数为 $2^{32}$ 过大,需用龟速乘。

但毕竟已经允许用 __int128 了,所以就偷个懒吧。

```cpp
#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;
}
```

标签:ch,TJOI2015,P3977,int,题解,矩阵,th,fi,se
From: https://www.cnblogs.com/gmtfff/p/p3977.html

相关文章