「TJOI2015」棋盘

该博客探讨了在3*p的攻击矩阵中,如何在第1行第k列放置棋子,使得棋子之间不会互相攻击。通过观察数据范围,使用状态压缩和矩阵快速幂的方法优化解题,时间复杂度为O(m^3 log n)。文章包含问题描述、解决方案和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Link
可以先看看大佬的博客,我是看了他的做法才会做的。

题意

给出一个 3 ∗ p 3*p 3p的攻击矩阵,棋子在第 1 1 1行,第 k k k列。阅读理解开始了,矩阵是从 0 0 0开始标号的,也就是说棋子在中间那一行,要在一个 n ∗ m n*m nm的矩阵上摆棋子,问你有多少种摆法能让棋子互相不攻击到。
观察数据范围 n ≤ 1 e 6 , m ≤ 6 n \leq 1e6,m \leq 6 n1e6,m6发现可以状压,显然每一行只跟上一行有关,预处理出这行的摆法下一行能摆什么(位运算),然后矩阵快速幂优化一下就行了,时间复杂度 O ( m 3 l o g n ) O(m^3logn) O(m3logn)
b a s e [ i ] [ j ] = 1 base[i][j]=1 base[i][j]=1表示 j j j能摆在 i i i的下一行(二进制形式),最后乘上一个 o n e [ 1 ] [ 1 ] = 1 one[1][1] = 1 one[1][1]=1的矩阵,表示一个虚拟的第零行,第零行下可以摆任意一行,累加得到答案。

代码

#include<bits/stdc++.h>
#define int unsigned long long
#define N 77
#define mod (unsigned long long)(1ull<<32)
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n;i>=a;i--)
#define inf 0x3f3f3f3f
#define pb push_back
#define mp make_pair
#define lowbit(i) ((i)&(-i))
#define VI vector<int>
using namespace std;
int n,m,p,k,tot,atk[3][N],a[3],zt[N],t[N][N]; 
struct mat{
	int M[N][N];
	void operator *=(const mat&o){
		rep(i,1,tot+1) rep(j,1,tot+1) t[i][j] = 0;
		rep(i,1,tot+1){
			rep(k,1,tot+1){
				rep(j,1,tot+1){
					t[i][j] = t[i][j]+(M[i][k]*o.M[k][j])%mod; 
					t[i][j] %= mod;
				}
			}
		}
		rep(i,1,tot+1) rep(j,1,tot+1) M[i][j] = t[i][j]; 
	}
}res,base,one;
signed main(){
	//freopen(".in","r",stdin);
	//freopen(".out","w",stdout);
	scanf("%lld%lld%lld%lld",&n,&m,&p,&k);
	rep(i,0,3){
		rep(j,0,p){
			int te;
			scanf("%lld",&te); a[i] += (1<<j)*te;
		}
	}
	a[1] -= (1<<k);zt[++tot] = 0;int sum = (1<<m);
	rep(i,1,sum){
		for(int j = 0,p = i;p;p >>= 1,j++){
			if((p&1)==0) continue;
			atk[0][i] |= (j<k)?a[0]>>(k-j):a[0]<<(j-k); 
			atk[1][i] |= (j<k)?a[1]>>(k-j):a[1]<<(j-k); 
			atk[2][i] |= (j<k)?a[2]>>(k-j):a[2]<<(j-k); 
		}
	}
	rep(i,1,sum)
		if((i&atk[1][i])==0)
			zt[++tot] = i;
//	cout << tot << endl;
	rep(i,1,tot+1){
		rep(j,1,tot+1){
			if((atk[2][zt[i]]&zt[j])==0&&(atk[0][zt[j]]&zt[i])==0)
				base.M[i][j]++;//,cout << i << ' ' << j << endl;
		}
	}
	rep(i,1,tot+1) res.M[i][i] = 1;one.M[1][1] = 1;
	for(;n;n>>=1,base*=base)
		if(n&1) res *= base;
	one *= res;
	int ans = 0;
	rep(i,1,tot+1){ans = (ans+one.M[1][i])%mod;} printf("%lld",ans);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值