【2021 HDU多校集训第一场】03. Puzzle loop

Description

给你一个n*m的网格图,你需要在这个网格图上选择若干条边,需要满足以下条件:

  1. 所有选出来的边需要构成若干个环,每条被选中的边必须在至少一个环内;
  2. 环与环只能存在交点,不能存在交边;

同时网格图上每个格子还有一个权值 a [ i ] [ j ] = 0 / 1 / − 1 a[i][j]=0/1/-1 a[i][j]=0/1/1,表示与这个格子相邻的4条边中,必须要有偶数个或者奇数个的边被选中,-1表示无限制。

求所有选边的方案数,mod 998244353,
2 < = n , m < = 17 2<=n,m<=17 2<=n,m<=17
在这里插入图片描述

Solution

一眼看上去怎么看都是插头DP,
结果正解是高斯消元你敢信???

因为必须要构成环,我们考虑用格子的选中状态来表示边的选中状态,
即,我们选择若干个格子,一堆联通的格子的外轮廓就是我们选择的边,这样可以表示出所有可能的选法。

转化为这样,题目就很好做了。
某一条边是被选中的,当且仅当这条边相邻的两个格子恰好被选中一个,
于是我们就可以列出许多的异或方程,用高斯消元找自由元个数即可。
答案一定是2的次幂。

复杂度: O ( ( n m ) 3 / 32 ) O((nm)^3/32) O((nm)3/32)

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
#define JS(i,j) ((i)*m-m+(j))
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int N=20,mo=998244353;

int read(int &n)
{
	bool q=0;n=0;char ch=' ';
	for(;ch!='-'&&(ch<'0'||ch>'9');ch=getchar());
	if(ch=='-')ch=getchar(),q=1;
	for(;ch>='0'&&ch<='9';ch=getchar())n=(n<<3)+(n<<1)+ch-48;
	return q?n=-n:n;
}

int n,m,ans;
int a[N][N];
bitset<N*N>b[N*N];
int b0;
int CAL(int n,int m)
{
	int la=1;
	fo(I,1,m)
	{
		int q=la;
		for(;q<=n&& !b[q][I];++q);
		if(q>n)continue;
		if(q>la)
		{
			swap(b[la],b[q]);
		}
		fo(i,la+1,n)if(b[i][I])
		{
			b[i]^=b[la];
		}
		++la;
	}
	int ans=0;
	fo(i,1,n)if(b[i].count())
	{
		++ans;
		if(b[i].count()==1 && b[i][0]==1)return -1;
	}
	return m-ans;
}
int main()
{
	freopen("!.in","r",stdin);
	freopen("1.out","w",stdout);
	int q,w,_;
	read(_);
	while(_--)
	{
		read(n),read(m);
		--n,--m;
		fo(i,1,n)
		{
			char ch=' ';
			for(;ch!='.'&&(ch<'0'||ch>'9');ch=getchar());
			fo(j,1,m)
			{
				if(ch=='0')a[i][j]=2;
				else if(ch=='1')a[i][j]=1;
				else a[i][j]=0;
				ch=getchar();
			}
		}
		b0=0;
		fo(i,1,n)fo(j,1,m)if(a[i][j])
		{
			++b0;
			b[b0][0]=a[i][j]%2;
			if(i>1)b[b0][JS(i-1,j)]=1;
			if(i<n)b[b0][JS(i+1,j)]=1;
			if(j>1)b[b0][JS(i,j-1)]=1;
			if(j<m)b[b0][JS(i,j+1)]=1;
		}
		ans=CAL(b0,n*m);

		if(ans<0)printf("0\n");
		else {
			LL t=1;
			for(;ans;--ans)t=(t<<1)%mo;
			printf("%lld\n",t);
		}

		fo(i,1,b0)b[i].reset();
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值