[CQOI2012]局部极小值

190 篇文章 2 订阅
84 篇文章 2 订阅

题面

题意

将1,2,3…,m*n填入一个 m ∗ n m*n mn的矩阵,要求一些位置满足比周围八个数都小,且其他位置不满足,则一共有几种方案.

做法

首先不考虑"其他位置不满足"这个要求,因为数据范围很小,因此最多有8个位置符合条件,可以状压dp.考虑将 m ∗ n m*n mn个数从小到大依次填入矩阵,用 d p [ i ] [ j ] dp[i][j] dp[i][j]表示当前要求位置填的数的状态为i,其他位置一共填了j个的方案数,即可求出答案.
然后考虑其他位置,可以进行容斥,暴力搜索出哪些位置也满足比周围的数都小,然后用上述dp即可求出方案数,从而得到答案.

代码

#include<bits/stdc++.h>
#define ll long long
#define N 10
#define M 12345678
using namespace std;

ll m,n,ans,x[N],y[N],dp[260][30];
char mm[N][N];

inline void Add(ll &u,ll v){u=(u+v)%M;}
inline void GG()
{
	puts("0");
	exit(0);
}

inline ll calc()
{
	ll i,j,k,ii,jj,cx=0;
	bool ok[N][N];
	for(i=1;i<=m;i++)
	{
		for(j=1;j<=n;j++)
		{
			if(mm[i][j]=='X')
			{
				cx++;
				x[cx]=i;
				y[cx]=j;
			}
		}
	}
	for(i=0;i<(1 << cx);i++) memset(dp[i],0,sizeof(dp[i]));
	dp[0][0]=1;
	for(i=0;i<(1 << cx);i++)
	{
		memset(ok,1,sizeof(ok));
		ll cnt=0;
		for(j=1;j<=cx;j++)
		{
			if((1 << (j-1))&i) continue;
			for(ii=x[j]-1;ii<=x[j]+1;ii++)
			{
				for(jj=y[j]-1;jj<=y[j]+1;jj++)
				{
					ok[ii][jj]=0;
				}
			}
		}
		for(ii=1;ii<=m;ii++)
		{
			for(jj=1;jj<=n;jj++)
			{
				if(mm[ii][jj]=='X') continue;
				cnt+=ok[ii][jj];
			}
		}
		for(j=0;j<=m*n-cx;j++)
		{
			if(!dp[i][j]) continue;
			for(k=1;k<=cx;k++)
			{
				if((1 << (k-1))&i) continue;
				Add(dp[i|(1 << (k-1))][j],dp[i][j]);
			}
			if(j<cnt) Add(dp[i][j+1],dp[i][j]*(cnt-j)%M);
		}
	}
	return dp[(1 << cx)-1][m*n-cx];
}

void dfs(ll u,ll v,ll zf)
{
	if(u>m)
	{
		Add(ans,M+zf*calc());
		return;
	}
	ll i,j;
	if(v==n) dfs(u+1,1,zf);
	else dfs(u,v+1,zf);
	for(i=u-1;i<=u+1;i++)
	{
		for(j=v-1;j<=v+1;j++)
		{
			if(mm[i][j]=='X') break;
		}
		if(j<=v+1) break;
	}
	if(i>u+1)
	{
		mm[u][v]='X';
		if(v==n) dfs(u+1,1,-zf);
		else dfs(u,v+1,-zf);
		mm[u][v]='.';
	}
}

int main()
{
	ll i,j;
	cin>>m>>n;
	for(i=1;i<=m;i++) scanf("%s",mm[i]+1);
	for(i=1;i<=m;i++)
	{
		for(j=1;j<=n;j++)
		{
			if(mm[i][j]=='X' && mm[i-1][j]=='X') GG();
			if(mm[i][j]=='X' && mm[i][j-1]=='X') GG();
		}
	}
	dfs(1,1,1);
	cout<<ans;
}

根据引用所述,交错序列是一个仅由0和1构成的序列,其中没有相邻的1(可以有相邻的0)。特征值定义为x^ay^b,其中x和y分别表示0和1出现的次数。长度为n的交错序列可能有多个。问题要求计算所有长度为n的交错序列特征值的和除以m的余数。 根据引用所述,输入文件包含一个行,该行包含三个整数n、a、b和m。其中,1≤n≤10000000,0≤a、b≤45,m<100000000。 为了解决这个问题,可以使用动态规划和矩阵快速幂优化的方法,具体实现可以参考引用提到的相关算法。算法的思路是通过计算长度为n的交错序列的特征值,然后将所有特征值求和并对m取余数。 具体步骤如下: 1. 使用动态规划计算长度为n的所有交错序列的特征值,将结果保存在一个矩阵中。 2. 使用矩阵快速幂优化,将动态规划的过程进行优化。 3. 对优化后的结果进行求和,并对m取余数。 4. 输出结果。 参考引用给出的博客中的代码实现,可以帮助你更好地理解和实现该算法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [BZOJ5298 CQOI2018 交错序列 【DP+矩阵快速幂优化】*](https://blog.csdn.net/weixin_30892987/article/details/99470493)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值