题目大意:给定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; }