[BZOJ3198][Sdoi2013]spring(hash+容斥原理+组合数学)

题目描述

传送门

题解

可以通过枚举+hash求出有i位对应相同有多少对
设其为f(i)
那么答案应该为 f(k)Ckkf(k+1)Ckk+1...f(6)Ck6
容斥系数是组合数的原因是即使不考虑有i为对应相同的和有i+1位对应相同的有交集,还是会选出很多重复的情况,所以应该同时将其去重

刚开始hash挂了一个map,T成狗…
实际上排个序就能快很多

代码

#include
#include
#include
#include
#include
#include
using namespace std;
#define LL long long
#define UL unsigned long long

const UL S=200001001LL;
int n,k;
int a[100005][10],cnt[100],l[10],r[10];
LL mul[10],now,sum,ans;UL mi[10],hash[100005];
struct data{int cnt,val;}sta[100];
map  mp;

int cmp(data a,data b)
{
    return a.cntint main()
{
    scanf("%d%d",&n,&k);
    mi[0]=1;for (int i=1;i<=6;++i) mi[i]=mi[i-1]*S;
    mul[0]=1;for (int i=1;i<=6;++i) mul[i]=mul[i-1]*(LL)i;
    for (int i=1;i<=n;++i)
        for (int j=5;j>=0;--j)
            scanf("%d",&a[i][j]);
    for (int i=1;i<1<<6;++i)
    {
        sta[i].val=i;
        for (int j=0;j<6;++j)
            if ((i>>j)&1) ++sta[i].cnt;
    }
    sort(sta+1,sta+(1<<6),cmp);
    for (int i=1;i<1<<6;++i)
    {
        if (sta[i].cnt!=sta[i-1].cnt) l[sta[i].cnt]=i;
        if (sta[i].cnt!=sta[i+1].cnt) r[sta[i].cnt]=i;
    }
    for (int i=k,f=1;i<=6;++i,f=-f)
    {
        sum=0;
        if (!i)
        {
            ans+=(LL)n*(n-1)/2*f;
            continue;
        }
        for (int p=l[i];p<=r[i];++p)
        {
            int val=sta[p].val;
            for (int j=1;j<=n;++j)
            {
                hash[j]=0;
                for (int q=0;q<6;++q)
                    if ((val>>q)&1) hash[j]+=(UL)a[j][q]*mi[q];
            }
            now=0;
            sort(hash+1,hash+n+1);
            for (int j=1;j<=n;++j)
                if (hash[j]==hash[j-1]) ++now;
                else sum+=now*(now-1)/2,now=1;
            sum+=now*(now-1)/2;
        }
        ans+=sum*mul[i]/mul[i-k]/mul[k]*f;
    }
    printf("%lld\n",ans);
}

你可能感兴趣的:(题解,hash,容斥原理,组合数学)