题目大意:给定n个元素,每个元素是一个六元组,求有多少对元素满足相同的位置恰好有k个
首先对于恰好有K个这种东西果断考虑容斥原理
我们2^6枚举相同的位置
恰好有k个元素相同的对数=至少有k个位置相同的对数-至少有k+1个位置相同的对数+至少有k+2个位置相同的对数……
但是我们计数时会发现一些问题 比如下面这组样例显然是0:
2 3
1 2 3 4 5 5
1 2 3 4 6 6
但是这一对元素被加了C(4,3)次,只被减掉了C(4,4)次
因此我们将公式改成这样:
恰好有k个元素相同的对数=至少有k个位置相同的对数*C(k,k)-至少有k+1个位置相同的对数*C(k+1,k)+至少有k+2个位置相同的对数*C(k+2,k)……
这样就行了
至于统计同一组位置上有多少对相同的元素,可以利用哈希表
首先将这最多6个数利用RK的思想Hash成一个数,然后插入Hash表即可
时间复杂度O(n*2^6)
如果将哈希表改成排序,时间复杂度O(nlogn*2^6),会TLE
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 100100
#define BASE 999911657
using namespace std;
const int C[7][7]={
{1,0,0,0,0,0,0},
{1,1,0,0,0,0,0},
{1,2,1,0,0,0,0},
{1,3,3,1,0,0,0},
{1,4,6,4,1,0,0},
{1,5,10,10,5,1,0},
{1,6,15,20,15,6,1},
};
int n,k;
int a[M][6];
int digit[64];
long long ans;
namespace Hash_Set{
struct List{
List *next;
unsigned long long hash;
int val;
List() {}
List(List *_,unsigned long long __):
next(_),hash(__),val(0) {}
}*head[1001001],mempool[M],*C=mempool;
int tim[1001001],T;
inline void Initialize()
{
++T;C=mempool;
}
int& Hash(unsigned long long hash)
{
int pos=hash%1001001;
if(tim[pos]!=T)
tim[pos]=T,head[pos]=0x0;
for(List *temp=head[pos];temp;temp=temp->next)
if(temp->hash==hash)
return temp->val;
head[pos]=new (C++) List(head[pos],hash);
return head[pos]->val;
}
}
long long Calculate(int state)
{
using namespace Hash_Set;
int i,j;
long long re=0;
Initialize();
for(i=1;i<=n;i++)
{
unsigned long long hash=0;
for(j=1;j<=6;j++)
if( state&(1<<j-1) )
(hash*=BASE)+=a[i][j];
int &val=Hash(hash);
re+=(val++);
}
return re;
}
int main()
{
int i,j;
cin>>n>>k;
for(i=1;i<=n;i++)
for(j=1;j<=6;j++)
scanf("%d",&a[i][j]);
for(i=0;i<64;i++)
{
digit[i]=digit[i>>1]+(i&1);
if(digit[i]>=k)
ans+=(digit[i]-k&1?-1:1)*Calculate(i)*C[digit[i]][k];
}
cout<<ans<<endl;
return 0;
}