BZOJ 3198 SDOI2013 spring

时间:2023-03-09 09:41:03
BZOJ 3198 SDOI2013 spring

为什么SDOI省选一年考两次容斥原理?

我们很容易发现>=k个相等时很好计算的

但是我们要求恰好k个,那么我们容斥即可

至于计算>=k个相等,首先我们枚举相等位置,对每个串对应位置做一遍hash就可以了

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
using namespace std; typedef long long LL;
const int maxn=100010;
const int x=1333331;
const int mod=100007;
int n,k,lim;
int Num[maxn];
int jc[maxn];
LL A[6][maxn];
LL Hash[maxn];
LL ans; void read(int &num){
num=0;char ch=getchar();
while(ch<'!')ch=getchar();
while(ch>='0'&&ch<='9')num=num*10+ch-'0',ch=getchar();
}
struct HASMMAP{
int h[mod+10],next[200010],cnt;
int val[200010];
LL S[200010];
void init(){memset(h,0,sizeof(h));cnt=0;}
void push(LL Hash){
int key=Hash%mod;
if(key<0)key+=mod;
for(int i=h[key];i;i=next[i]){
if(S[i]==Hash){val[i]++;return;}
}
++cnt;next[cnt]=h[key];h[key]=cnt;
S[cnt]=Hash;val[cnt]=1;
}
int ask(LL Hash){
int key=Hash%mod;
if(key<0)key+=mod;
for(int i=h[key];i;i=next[i]){
if(S[i]==Hash)return val[i];
}return 0;
}
}H;
LL Get_ans(int k){
H.init();
LL sum=0;
for(int i=1;i<=n;++i){
LL now=0;
for(int j=0;j<6;++j){
if(k>>j&1)now=now*x+A[j][i];
}
sum+=H.ask(now);
H.push(now);
}return sum;
}
LL C(int n,int m){return jc[n]/jc[m]/jc[n-m];}
int Get_Num(int k){
int cnt=0;
for(int i=0;i<6;++i)if(k>>i&1)cnt++;
return cnt;
} int main(){
read(n);read(k);
for(int i=1;i<=n;++i)for(int j=0;j<6;++j)scanf("%lld",&A[j][i]);
jc[0]=1;
for(int i=1;i<=6;++i)jc[i]=jc[i-1]*i;
lim=(1<<6);
for(int i=0;i<lim;++i){
Num[i]=Get_Num(i);
if(Num[i]<k)continue;
if((Num[i]-k)&1)ans=ans-1LL*C(Num[i],k)*Get_ans(i);
else ans=ans+1LL*C(Num[i],k)*Get_ans(i);
}printf("%lld\n",ans);
return 0;
}