题解:AT_arc197_d [ARC197D] Ancestor Relation
题目传送门
思路
好题要赞,建议评蓝。
题目描述
给你一个
N\times N 的矩阵A ,其中每一项只会是0 或1 。请找出有多少种不同的
N 个点的树G ,使得:两棵树不同,当且仅当存在两个结点 $i,j$,在其中一棵树上它们之间有直接连边而在另一棵树上没有。 请输出答案对 $998244353$ 取模后的结果。
这题显而易见的位运算。
我们令
根据题目定义,我们有若
定义
我们有如下结论:
结论
证明:
与子树内节点的节点一定在子树内的点到根节点
同理,我们可以举出“与祖先有关系的节点一定与子树内节点有关系。”的反例。例如,我们设祖先为
所以我们有公式,一下钦定
- 若
a_{i,j}=a_{j,i}=1 ,则s_i 一定包含s_j ,即s_i \mid s_j =s_i 。 - 若
a_{i,j}=a_{j,i}=0 ,则一定不存在s_i \mid s_j =s_i 。
同时还有
接下来考虑有解情况。根据上面的推导,我们发现交换
所以预处理一下阶乘,在算一下即可。
bitset 维护做到了
代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=405;
const int mod=998244353;
int t,n,jc[N];
bool vis[N];
signed main(){
jc[0]=1;
for(int i=1;i<=400;i++) jc[i]=jc[i-1]*i%mod;
scanf("%lld",&t);
while(t--){
scanf("%lld",&n);
memset(vis,0,sizeof(vis));
bitset <N> s[N];
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
for(int j=1,x;j<=n;j++)
scanf("%lld",&x),s[i][j]=x;
bool f=0;
for(int i=1;i<=n;i++)
if(!s[i][1]||!s[1][i]){f=1;break;}
if(f==1){printf("0\n");continue;}
for(int i=2;i<=n;i++){
for(int j=2;j<=n;j++)
if((((s[i]|s[j])==s[i])||((s[i]|s[j])==s[j]))&&!s[i][j]){f=1;break;}
else if((s[i]|s[j])!=s[i]&&(s[i]|s[j])!=s[j]&&s[i][j]){f=1;break;}
if(f==1) break;
}
if(f==1){printf("0\n");continue;}
int ans=1;
for(int i=2;i<=n;i++)
if(!vis[i]){
int cnt=1;
for(int j=i+1;j<=n;j++)
if(s[i]==s[j]) cnt++,vis[j]=1;
ans=ans*jc[cnt]%mod;
}
printf("%lld\n",ans);
}
return 0;
}
撒花!