专栏文章
题解:AT_arc197_d [ARC197D] Ancestor Relation
AT_arc197_d题解参与者 1已保存评论 0
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 0 条
- 当前快照
- 1 份
- 快照标识符
- @mimyzc0r
- 此快照首次捕获于
- 2025/12/01 17:51 3 个月前
- 此快照最后确认于
- 2025/12/01 17:51 3 个月前
思路
好题要赞,建议评蓝。
题目描述
给你一个 的矩阵 ,其中每一项只会是 或 。请找出有多少种不同的 个点的树 ,使得:,当且仅当 ,或以结点 为根时, 是 的祖先或 是 的祖先。两棵树不同,当且仅当存在两个结点 ,在其中一棵树上它们之间有直接连边而在另一棵树上没有。请输出答案对 取模后的结果。
这题显而易见的位运算。
我们令 的矩阵为 ,则不妨用一个 表示每一行中 的 或 总体情况。我们令这个 为 。
更具题目定义,我们有若 ,则 和 为祖先关系,即要么 是 的祖先,要么 是 的祖先。所以 和 同链。因为题目说 为根,则 。可以判掉一部分无解情况。
定义 和 有关系当且仅当 。
我们有如下结论:
结论 :与子树内节点有关系的节点一定与祖先有关系。
结论 :与祖先有关系的节点不一定与子树内节点有关系。
结论 :与祖先有关系的节点不一定与子树内节点有关系。
证明:
与子树内节点的节点一定在子树内的点到根节点 的唯一路径上(唯一链),而祖先一定处于这个位置(祖先的定义),所以结论 成立。
同理,我们可以举出“与祖先有关系的节点一定与子树内节点有关系。”的反例。例如,我们设祖先为 ,若 节点分叉,随意选 条链即可举出反例。
所以我们有公式,一下钦定 处于 上面:
- 若 ,则 一定包含 ,即 。
- 若 ,则一定不存在 。
同时还有 的情况。则可以再次判掉剩下的无解情况。
接下来考虑有解情况。根据上面的推导,我们发现交换 和 的位置不会改变情况,假设链上有 个,很明显排列组合一下情况数有 。
所以预处理一下阶乘,在算一下即可。
维护做到了 。可以尝试推导一下时间复杂度。
代码
CPP#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;
}
撒花!
相关推荐
评论
共 0 条评论,欢迎与作者交流。
正在加载评论...