Adore——解题报告

题目链接http://192.168.10.251/problem/105
题目大意:给定一个m层的分层图,只有从i层连向i+1层的,除第一层和第m层点数为1外,其余各层点数为k。 现在小 w 每次可以取反第 i(1<i<n−1)层和第 i+1层之间的连边。也就是把原本从(i,k1)连到(i+1,k2)的边,变成从(i,k2)连到(i+1,k1)。请问他有多少种取反的方案,把从源点到汇点的路径数变成偶数条? 答案对 998244353取模。
数据范围:4≤m≤10^4,k≤10
题目分析
1.通过仔细思考我们可以发现,对于到某一个点的方案数的奇偶性,只跟上一层与这个点相连的点有关。所以我们可以通过枚举每一层之间相连的状态来从第i层,转移到第i+1层。
2.观察数据范围,k≤10,这很明显是一个可以状压的数据范围,因为本题只考虑路径方案数的奇偶性,所以可以将状态表示为:0表示偶数种路径,1表示奇数种路径,于是这道题就转换成了一道状压dp的题。
解题过程
1.按层枚举当前层的状态,考虑对于当前层第i个点,本层的边正向和反向的不同状态影响。
2.将枚举的状态i并上第j个点状态影响情况:i&a[j]i&b[j]。表示只考虑这一个点的情况,于是我们就可以得到一个新的状态表示从上一层的所有点到点j的状态。我们提前预处理出cnt数组,表示当前状态的路径奇偶性。
3.得到新的奇偶性后,右移对应位数作为新一层的状态的一部分x|=(cnt[i&a[j]]<<(j-1))
4.当前层的值由上一层推导:dp[floor][x]=(dp[floor][x]+dp[floor-1][i])%mod
5.打印结果。

正解代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define mod 998244353

using namespace std;
typedef long long ll; 
const ll maxm =1e4+10,maxk=11;
ll dp[2][(1<<maxk)+10],cnt[(1<<maxk)+10];
ll zheng[maxk+10],fan[maxk+10];
ll x;
int main()
{
	ll m,k;
	scanf("%lld%lld",&m,&k);
	ll start=0;
	for(ll i=1;i<=k;i++)
	{
		scanf("%lld",&x);
		start|=(x<<(i-1));
	}
	dp[0][start]=1;
	ll Max=(1<<k)-1;
	for(ll i=0;i<=Max;i++)
		cnt[i]=cnt[i>>1]^(i&1);
	ll flag=0;
	for(ll nouse=2;nouse<=m-2;nouse++)
	{
		memset(zheng,0,sizeof(zheng));
		memset(fan,0,sizeof(fan));
		flag^=1;
		memset(dp[flag],0,sizeof(dp[flag]));
		for(ll i=1;i<=k;i++)
		{
			for(ll j=1;j<=k;j++)
			{
				scanf("%lld",&x);
				zheng[i]|=(x<<(j-1));
				fan[j]|=(x<<(i-1));
			}
		}
		for(ll i=0;i<=Max;i++)
		{
			if(dp[flag^1][i])
			{
				ll temp1=0,temp2=0;
				for(ll j=1;j<=k;j++)
				{
					temp1|=(cnt[i&zheng[j]]<<(j-1));
					temp2|=(cnt[i&fan[j]]<<(j-1));
				}
				dp[flag][temp1]=(dp[flag][temp1]+dp[flag^1][i])%mod;
				dp[flag][temp2]=(dp[flag][temp2]+dp[flag^1][i])%mod;
			}
		}
	}
	start=0;
	for(ll i=1;i<=k;i++)
	{
		scanf("%lld",&x);
		start|=x<<(i-1);
	}
	ll ans=0;
	for(ll i=0;i<=Max;i++)
		(ans+=cnt[start&i]?0:dp[flag][i])%=mod;
	printf("%lld\n",ans);
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值