bzoj3198

容斥原理+哈希表

恰好k个,那么上容斥原理,我们先2^6枚举相同的位置,用哈希表判断有多少个对应位置相同的元素,然后用容斥原理计算,似乎这里的容斥没有那么简单,详见这里 http://www.cnblogs.com/candy99/p/mobius.html, 要乘上组合数计算

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 100010, mod = 100007;
int n, k;
ll ans;
int c[8][8], a[N][6];
namespace Hash_List {
    int cnt, tot;
    int h[N], bel[N];
    struct List {
        int nxt, id, cnt;
    } e[N << 6];
    void ini() { ++cnt; }
    bool same(int x, int y, int s) {
        for(int i = 0; i < 6; ++i) if(s & (1 << i) && a[x][i] != a[y][i]) return false;
        return true;    
    }
    int hash(int *a, int s) {
        int h = 0;
        for(int i = 0; i < 6; ++i) if(s & (1 << i)) h = ((h * 12321 % mod + a[i] % mod) % mod + mod) % mod;
        return h;
    }
    int insert(int id, int s) {
        int val = hash(a[id], s);
        if(bel[val] != cnt) h[val] = 0, bel[val] = cnt; // 如果这个没有被标记过,那么就暴力查找,否则直接插入 
        for(int i = h[val]; i; i = e[i].nxt) if(same(e[i].id, id, s)) { ++e[i].cnt; return e[i].cnt - 1; }
        e[++tot] = (List) {h[val], id, 1}, h[val] = tot;        
        return 0;   
    }
} using Hash_List :: insert; using Hash_List :: ini;
int one(int x) {
    int ret = 0;
    while(x) ++ret, x &= (x - 1);
    return ret;
}
int main()
{
    scanf("%d%d", &n, &k);  
    c[0][0] = 1;
    for(int i = 1; i <= 7; ++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 = 1; i <= n; ++i) 
        for(int j = 0; j < 6; ++j) scanf("%d", &a[i][j]);
    for(int i = 0; i < 64; ++i) 
    {
        int t = one(i);
        if(t < k) continue;
        Hash_List :: ini();
        for(int j = 1; j <= n; ++j) ans += (((t - k) & 1) ? -1 : 1) * (ll)Hash_List :: insert(j, i) * (ll)c[t][k];           
    }
    printf("%lld\n", ans);  
    return 0;
}
View Code

 

posted @ 2017-10-20 10:44  19992147  阅读(208)  评论(0编辑  收藏  举报