【矩阵快速幂】【状态压缩】【动态规划】lydsy4000 [TJOI2015]棋盘

4000: [TJOI2015]棋盘
Time Limit: 10 Sec Memory Limit: 128 MB
Submit: 643 Solved: 314
[Submit][Status][Discuss]
Description
a

Input
输入数据的第一行为两个整数N,M表示棋盘大小。第二行为两个整数P,K,
表示攻击范围模板的大小,以及棋子在模板中的位置。接下来三行,
每行P个数,表示攻击范围的模版。每个数字后面一个空格。
Output
一个整数,表示可行方案Mod 2 ^32

Sample Input
2 2

3 1

0 1 0

1 1 1

0 1 0
Sample Output
7
HINT
1<=N<=10^6,1<=M<=6


 这两天学习和代码的状态都很混乱,感觉是一个阵痛期。这题本身不难,当做一个模板记录题吧。
 题意很显然(需要注意题目中的行列都是从0开始标号的)。注意到数据范围n很大而m很小,猜测出是dp,然后可以用1<<m个数字的二进制代表所有状态。由于每次状态转移方式固定不变,所以可以先预处理出转移矩阵,再用矩阵快速幂来进行加速。
 预处理时,我们先判断两个状态本身是否合法(同一行不互相攻击),然后对合法的状态之间尝试连边,使得两个状态没有棋子互相攻击。
 矩阵乘法时,答案应为AF^n的各个元素和,其中A是一个只有无棋子状态为1,其余状态为0的1(1<<m)的矩阵。为了方便,我们直接把第一行的和求出来就行了。
 另外,题目里要求对1LL<<32取模,可以直接对unsigned溢出,进一步,可以直接对int溢出,输出时转换为unsigned即可,这是因为int是按照补码存储的,本质就是一个unsigned。

#include<cstdio>
#define bit(x,i) (x>>i-1&1)
using namespace std;

struct Matrix
{
	int P,Q,M[65][65];
	
	inline void operator = (const Matrix &t) 
	{
		P=t.P;
		Q=t.Q;
		for(int i=1;i<=P;i++)
			for(int j=1;j<=Q;j++)
				M[i][j]=t.M[i][j];
	}
	
	void times(Matrix &a, Matrix &b)
	{
		if(a.Q!=b.P)
			return ;
		P=a.P;
		Q=b.Q;
		for(int i=1;i<=P;i++)
			for(int j=1;j<=b.Q;j++)
			{
				M[i][j]=0;
				for(int k=1;k<=a.Q;k++)
					M[i][j]+=a.M[i][k]*b.M[k][j];
			}
	}
	
	void quick_power(int t)
	{
		Matrix res[2],base[2];
		int o=0,p=0;
		res[p]=*this;
		base[o]=*this;
		--t;
		while(t)
		{
			if(t&1)
			{
				res[p^1].times(res[p],base[o]);
				p^=1;
			}
			base[o^1].times(base[o],base[o]);
			o^=1;
			t>>=1;
		}
		*this=res[p];
	}
}G;

int n,m,p,k,A[4][7];
unsigned ans;

bool check_valid(int x)
{
	for(int i=1;i<=m;i++) //枚举棋子对,看是否攻击
		if(bit(x,i))
			for(int j=1;j<=m;j++)
				if(i!=j&&bit(x,j))
					if(j-i+k>0&&j-i+k<=p&&A[2][j-i+k])
						return false;
	return true;
}

bool has_edge(int x, int y)
{
	for(int i=1;i<=m;i++) //看上面是否攻击了下面
		if(bit(x,i))
			for(int j=1;j<=m;j++)
				if(bit(y,j))
					if(j-i+k>0&&j-i+k<=p&&A[3][j-i+k])
						return false;
	for(int i=1;i<=m;i++) //看下面是否攻击了上面
		if(bit(y,i))
			for(int j=1;j<=m;j++)
				if(bit(x,j))
					if(j-i+k>0&&j-i+k<=p&&A[1][j-i+k])
						return false;
	return true;
}

int main()
{
	scanf("%d%d%d%d",&n,&m,&p,&k);
	k++;
	for(int i=1;i<=3;i++)
		for(int j=1;j<=p;j++)
			scanf("%d",&A[i][j]);
	G.P=G.Q=1<<m;
	for(int i=1;i<=(1<<m);i++)
		if(check_valid(i-1))
			for(int j=1;j<=(1<<m);j++)
				if(check_valid(j-1))
					G.M[i][j]=has_edge(i-1,j-1);
	G.quick_power(n);
	for(int i=1;i<=(1<<m);i++)
		ans+=G.M[1][i];
	printf("%u",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/BUAA_Alchemist/article/details/84845263