您的位置:首页 > 其它

JZOJ 5398. 【NOIP2017提高A组模拟10.7】Adore

2017-10-07 21:02 344 查看

题目

Description

小w 偶然间见到了一个DAG。

这个DAG 有m 层,第一层只有一个源点,最后一层只有一个汇点,剩下的每一层都有k 个节点。

现在小w 每次可以取反第i(1 < i < n - 1) 层和第i + 1 层之间的连边。也就是把原本从(i, k1) 连到(i + 1, k2) 的边,变成从(i, k2) 连到(i + 1, k1)。

请问他有多少种取反的方案,把从源点到汇点的路径数变成偶数条?

答案对998244353 取模。

Input

一行两个整数m, k。

接下来m - 1 行, 第一行和最后一行有k 个整数0 或1,剩下每行有k2 个整数0 或1,第(j- 1)* k + t 个整数表示(i, j) 到(i + 1, t)有没有边。

Output

一行一个整数表示答案。

Sample Input

5 3

1 0 1

0 1 0 1 1 0 0 0 1

0 1 1 1 0 0 0 1 1

0 1 1

Sample Output

4

Data Constraint

100% 的数据满足4 <= m <= 10^4, k <= 10。

题解

这道题很容易看错,是整行路径取反

我们发现这题的两个亮点。①k≤10,②奇偶性。

所以可以设装压DP。

设f[i][j]表示做到第i行,此层状态(即二进制第k位从源点走(i,k)的方案数的奇偶性)为j的方案数。

cnt[i]表示二进制i的1的个数的奇偶性。

我们知道了第i层不取反的走到下一层的点的对应的方案数的奇偶性。

a[i]表示此行第i个点能否走到下一行的点(不取反)。

b[i]表示此行第i个点能否走到下一行的点(取反)。

a0表示不取反,下一行的状态。(a1表取反)

那么f[i][a0]+=f[i][s],f[i][a1]+=f[i][s]。

a0=Σkj=1cnt[a[i]与s]∗2j−1

这表示什么意思?

如果第i+1行的某x个二进制位为1,说明到点(i+1,x)方案数为奇数。

走到(i,j)的方案数为奇数,且能够走到第i+1行的点(i+1,x),对走到(i+1,x)的方案数贡献为奇数。否则为偶数。

至于走到(i+1,x)的方案数则为上一行k个点走到(i+1,x)的方案数的总和。

所以二进制数a[i]&s的1的个数的奇偶性就代表走到(i+1,x)的方案数的奇偶性。

第1行和第n-1行要特殊一些,①不能将路径取反,②该行只有1个点。

第1行和第n-1行的转移与第2~n-2行类似。

总结一下

①虽然这是道很多人认为很简单的DP题,但我认为想起来不简单。

②值得推敲的:

每一行的取反/不取反可以通过DP的形式组成了一个个不重不漏的状态。

③即使我知道这是状压DP,但是算方案数的奇偶性还是挺恶心的一件事。

运算/状态转移方面有很大的问题。

解决方案:利用二进制的巧妙性。

(I)条件的合并:逻辑运算。

(II)奇数偶数加起来的和的奇偶性&二进制数的1的个数的奇偶性相结合。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 10010
#define LL long long
#define mo 998244353
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
LL _2[15];
LL i,j,k,l,n,m,x,o,ans;
LL s,a0,a1,a[15],b[15];
LL f
[1030],cnt[1030];
int main(){
freopen("adore.in","r",stdin);
freopen("adore.out","w",stdout);
_2[1]=1;fo(i,2,14)_2[i]=(_2[i-1]*2)%mo;
scanf("%lld%lld",&n,&k);
s=0;
fo(i,1,k){
scanf("%lld",&x);
s|=x*_2[i];
}
f[0][s]=1;o=0;
fo(i,0,_2[k+1]-1)cnt[i]=cnt[i/2]^(i&1);
fo(i,2,n-2){
o=1-o;
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
memset(f[o],0,sizeof(f[o]));
fo(j,1,k)fo(l,1,k){
scanf("%lld",&x);
if(x)a[j]|=_2[l],b[l]|=_2[j];
}
fo(s,0,_2[k+1]-1)
if(f[1-o][s]){
a0=a1=0;
fo(j,1,k){
a0|=cnt[s&a[j]]*_2[j];
a1|=cnt[s&b[j]]*_2[j];
}
f[o][a0]=(f[o][a0]+f[1-o][s])%mo;
f[o][a1]=(f[o][a1]+f[1-o][s])%mo;
}
}
o=1-o;
memset(f[o],0,sizeof(f[o]));
a[1]=0;
fo(i,1,k){
scanf("%lld",&x);
a[1]|=x*_2[i];
}
fo(s,0,_2[k+1]-1)
if(f[1-o][s]){
a0=cnt[s&a[1]]*_2[1];
f[o][a0]=(f[o][a0]+f[1-o][s])%mo;
}
printf("%lld",f[o][0]);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: