[Luogu P3298] [BZOJ 3198] [SDOI2013]泉

22 篇文章 0 订阅
6 篇文章 0 订阅
洛谷传送门
BZOJ传送门

题目描述

作为光荣的济南泉历史研究小组中的一员,铭铭收集了历史上 x x x个不同年份时不同泉区的水流指数,这个指数是一个小于 2 30 2^{30} 230的非负整数。第 i i i个年份时六个泉区的泉水流量指数分别为 A ( i , 1 ) , A ( i , 2 ) , A ( i , 3 ) , A ( i , 4 ) , A ( i , 5 ) A(i,1),A(i,2),A(i,3),A(i,4), A(i,5) A(i,1)A(i2)A(i3)A(i4)A(i5) A ( i , 6 ) A(i,6) A(i6)

现在铭铭希望知道有多少对不同的年份: i i i j j j,满足这两年恰好有 K K K个泉区的泉水流 S S S指数对应相同。

输入输出格式

输入格式:

第一行有 2 2 2个整数,分别是 N N N K K K

之后 N N N行,每行有 6 6 6个整数。第 i i i行的第 j j j个数字 A ( i , j ) A(i,j) A(ij)表示第 i i i个年份中第 j j j个泉区的泉水流量指数。

输出格式:

一个整数表示有多少对不同的年份满足恰有 K K K个区的泉水流量指数对应相同。

输入输出样例

输入样例#1:
3 3
1 2 3 4 5 6
1 2 3 0 0 0
0 0 0 4 5 6
输出样例#1:
2

说明

对于 100%的数据, 0 ≤ K ≤ 6 0\le K \le 6 0K6, 且所有数据中 K K K是等概率出现的, 即对于任意的 0 ≤ x 0\le x 0x都有大约 1 / 7 1/7 1/7 的数据中 K = x K=x K=x. N ≤ 100000 N\le 100000 N100000

解题分析

很显然的容斥, 我们先可以用哈希算出来至少有 K K K个, K + 1 K+1 K+1 6 6 6个相同的对数, 设这些值为 f ( K ) , f ( K + 1 ) . . . f ( 6 ) f(K),f(K+1)...f(6) f(K),f(K+1)...f(6)。再设 g ( K ) , g ( K + 1 ) , . . . , g ( 6 ) g(K),g(K+1),...,g(6) g(K),g(K+1),...,g(6)为恰好这么多相同的对数

显然, g ( K + 1 ) g(K+1) g(K+1) f ( K ) f(K) f(K)的贡献是 ( K + 1 K ) \binom{K+1}{K} (KK+1)次, 对 g ( K + 2 ) g(K+2) g(K+2)的贡献是 ( K + 2 K ) \binom{K+2}{K} (KK+2)次… ,大概是这样的:

f ( K ) = f(K)= f(K)= f ( K + 1 ) = f(K+1)= f(K+1)= f ( K + 2 ) = f(K+2)= f(K+2)=
g ( K ) g(K) g(K) ( K K ) \binom{K}{K} (KK)
g ( K + 1 ) g(K+1) g(K+1) ( K + 1 K ) \binom{K+1}{K} (KK+1) ( K + 1 K + 1 ) \binom{K+1}{K+1} (K+1K+1)
g ( K + 2 ) g(K+2) g(K+2) ( K + 2 K ) \binom{K+2}{K} (KK+2) ( K + 2 K + 1 ) \binom{K+2}{K+1} (K+1K+2) ( K + 2 K + 2 ) \binom{K+2}{K+2} (K+2K+2)

这是不能直接容斥的, 但观察可得每一列的下指标一样, 那么其中每一项乘上 ( K + m K ) \binom{K+m}{K} (KK+m)后, 就变成了:

K K K K + 1 K+1 K+1 K + 2 K+2 K+2
K K K ( K K ) ( 0 0 ) \binom{K}{K}\binom{0}{0} (KK)(00)
K + 1 K+1 K+1 ( K + 1 K ) ( 1 0 ) \binom{K+1}{K}\binom{1}{0} (KK+1)(01) ( K + 1 K ) ( 1 1 ) \binom{K+1}{K}\binom{1}{1} (KK+1)(11)
K + 2 K+2 K+2 ( K + 2 K ) ( 2 0 ) \binom{K+2}{K}\binom{2}{0} (KK+2)(02) ( K + 2 K ) ( 2 1 ) \binom{K+2}{K}\binom{2}{1} (KK+2)(12) ( K + 2 K ) ( 2 2 ) \binom{K+2}{K}\binom{2}{2} (KK+2)(22)

就可以容斥了…

auto真好用

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <vector>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define BASE 666233ll
#define MOD1 100003ll
#define ll long long
#define MOD2 19260817ll
#define MX 100500
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc);
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
}
struct INFO {int key, cnt;};
std::vector <INFO> buc[MOD1];
int n, k;
long long ans, res;
int dat[MX][6], C[7][7], tar[7];
IN void insert(ll key1, ll key2)
{
    for (auto &i : buc[key1]) if (i.key == key2) return i.cnt++, void();
    buc[key1].push_back({key2, 1});
}
IN void count()
{
    for (R int i = 0; i < MOD1; ++i)
    {
        for (auto j : buc[i]) res += 1ll * j.cnt * (j.cnt - 1) / 2;
        std::vector <INFO> ().swap(buc[i]);
    }
}
IN void work(R int typ)
{
    int tot = 0; ll hs1, hs2;
    for (R int i = 0; i < 6; ++i) if (typ & (1 << i)) tar[++tot] = i;
    for (R int i = 1; i <= n; ++i)
    {
        hs1 = hs2 = 0;
        for (R int j = 1; j <= tot; ++j)
        {
            hs1 = (hs1 * BASE % MOD1 + dat[i][tar[j]]) % MOD1;
            hs2 = (hs2 * BASE % MOD2 + dat[i][tar[j]]) % MOD2;
        }
        insert(hs1, hs2);
    }
    count();
}
int main(void)
{
    in(n), in(k);
    for (R int i = 1; i <= n; ++i)
    for (R int j = 0; j <  6; ++j) in(dat[i][j]);
    for (R int i = 0; i <= 6; ++i) C[i][0] = 1;
    for (R int i = 1; i <= 6; ++i)
    for (R int j = 1; j <= i; ++j)
    C[i][j] = C[i - 1][j - 1] + C[i - 1][j];
    for (int i = 0; i <= 63; ++i)
    {
        int cnt = __builtin_popcount(i);
        if (cnt < k) continue;
        res = 0; work(i);
        if ((k - cnt) & 1) ans -= res * C[cnt][k];
        else ans += res * C[cnt][k];
    }
    printf("%lld", ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值