CF1735D Meta-set题解
读一读题目
给出
五元集合法的条件是,能从中提取出合法三元集,数量超过
而三元集合法的条件是:对于三个串的第
举一点点例子
0 0 1 1
0 2 0 1
0 1 2 1
比如,对于这组由长度为
而对于以下的五元集:
0 0 0 0 // a串
0 0 0 1 // b串
0 0 0 2 // c串
0 0 1 0 // d串
0 0 2 0 // e串
由上面的规则可知,
想一想思路
要求出合法五元集的个数,我们首先得求出合法三元集的个数。
求合法三元集
用一个两重循环进行遍历,循环变量
为了保证三元集满足条件,可以通过已知的
0 0 1 // 第i串
0 2 0 // 第j串
我们从左向右逐位求解,第一位为
故三元集里的第三个串为
同时可以发现,已知两个串的前提下,三元集里的另一个串是唯一的。
于是,我们利用
求合法五元集
用一个循环遍历每一个三进制串。循环变量
为了使该五元集合法,五元集中必须能提取出至少两个的合法三元集。我们之前已经求出
代码
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int N = 1e3+4;
LL n,k,a[N][25],w[N],res,sum[N];
map <LL,int> mp;
LL make(LL p,LL q){
LL r=0;
for(LL j=1,t=1;j<=k;j++,t*=3){
if(a[p][j]==a[q][j]){
r+=a[p][j]*t;
}
else{
r+=(3-a[p][j]-a[q][j])*t;
}
}
return r;
}
int main(){
ios::sync_with_stdio(false);
cin>>n>>k;
for(LL i=1;i<=n;i++){
for(LL j=1,t=1;j<=k;j++,t*=3){
cin>>a[i][j];
w[i]+=a[i][j]*t; //记录每个三进制串对应的十进制数
}
mp[w[i]]=i; //记录该串对应的序号
}
//求合法三元集的个数
for(LL i=1;i<=n-2;i++){
for(LL j=i+1;j<n;j++){ //从i的下个串开始遍历,防止重复
LL num=make(i,j); //求i串和j串对应的第三个串
if(mp[num]>j){
//该串的序号应该大于j,从而保证不重复判断
sum[i]++;
sum[j]++;
sum[mp[num]]++;
}
}
}
//求合法五元集的个数,统计结果
for(LL i=1;i<=n;i++){
res+=sum[i]*(sum[i]-1)/2;
}
cout<<res<<'\n';
}