题目链接: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;
}