sdoi2013 spring(hash+容斥)

大体思路是先求出来\(f[i]\)代表有至少\(i\)个位置相同的点对数。
然后就已经没什么好害怕的了(跟BZOJ3622一样)
然后这个\(f[i\)]怎么求呢?
最无脑的方法就是枚举位置,然后\(hash\)表记一下每种情况出现多少次然后把\(\sum_{情况个数}{情况次数*(情况次数-1)}\)加到\(f[\)枚举的位置个数\(]\)。就行了。
发现这个方法复杂度足以通过此题。

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
#define int long long
const int N=101000;
const int mod=23333;
const int p=19260817;
int cnt,head[25000];
struct edge{
    int nxt,id,c;
}e[N];
int a[N][10],n,C[10][10],num,b[10],f[10],ans,mmp;
bool judge(int now,int x,int y){
    for(int i=1;i<=num;i++)
        if(a[x][b[i]]!=a[y][b[i]])return false;
    return true;
}
void add(int u,int id){
    cnt++;
    e[cnt].nxt=head[u];
    e[cnt].id=id;
    e[cnt].c=1;
    head[u]=cnt;
}
int ins(int now,int id,int x){
    for(int i=head[x];i;i=e[i].nxt)
        if(judge(now,e[i].id,id)){
            e[i].c++;
            return e[i].c-1;
        }
    add(x,id);
    return 0;
}
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
signed main(){
    n=read();mmp=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=6;j++)
            a[i][j]=read();
    C[0][0]=1;
    for(int i=1;i<=6;i++) {
        C[i][0]=1;
        for(int j=1;j<=i;j++)C[i][j]=C[i-1][j]+C[i-1][j-1];
    }
    for(int i=0;i<(1<<6);i++){
        num=0;
        memset(head,0,sizeof(head));cnt=0;
        for(int j=1;j<=6;j++)if(i&(1<<(j-1)))b[++num]=j;
        for(int j=1;j<=n;j++){
            int w=0;
            for(int k=1;k<=num;k++)
                w=(w*p%mod+a[j][b[k]]%mod)%mod;
            f[num]+=ins(i,j,w);
        }
    }
    int type=1;
    for(int i=mmp;i<=6;i++){
        ans+=type*C[i][mmp]*f[i];
        type=-type;
    }
    printf("%lld",ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Xu-daxia/p/10479850.html