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;
}

  

posted @ 2016-04-14 21:39  _Vertical  阅读(185)  评论(0编辑  收藏  举报