[题解] [Codechef] CNTDSETS

题面

题解

由于会出现重复的情况, 我们考虑如何去掉这种情况

我们可以这样考虑, 若每一维的最小值都为 \(0\) , 那么我们将这一种情况算作合法情况

同理, 若某几维的最小值不为 \(0\) , 我们必定可以经过平移将它的这一维移至 \(0\) , 所以这一种情况我们不算进去

这样就可以不重不漏的算出有多少个集合了, 并且该点集中每个点每一维都 \(\in [0, d]\)

考虑容斥计算总方案数, 设 \(f(d)\) 代表直径 \(\leq d\) 的点集有多少个
\[ \displaystyle f(d) = \sum_{i = 0}^{n}(-1)^i\binom{n}{i}2^{d^i(d+1)^{n-i}} \]
我们考虑枚举有至少 \(i\) 维差的最大值 \(< d\) , 从 \(n\) 各种选出 \(i\) 个, 有 \(\binom{n}{i}\) 种可能

又因为至少有 \(i\) 维最大值 \(< d\) , 故这一维一共有 \([1, d]\)\(d\) 种选法

其他的维每一维都有 \([0, d]\)\(d + 1\) 中选法

拼起来就有 \(d^i(d + 1)^{n - i}\) 种可以选的点, 每个点可选可不选, 所以有 \(2 ^ {d^i(d + 1)^{n - i}}\) 种选法

注意 \(2\) 的幂次要用欧拉定理取模

再乘上容斥系数 \((-1)^i\) 即可

Code

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
const int mod = 1000000007;
const int N = 1005; 
using namespace std;

int T, n, d, c[N][N]; 

template < typename T >
inline T read()
{
    T x = 0, w = 1; char c = getchar();
    while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
    while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * w; 
}

int fpow(int x, int y, int p)
{
    int res = 1;
    for( ; y; y >>= 1, x = 1ll * x * x % p)
        if(y & 1) res = 1ll * res * x % p; 
    return res; 
}

int f(int n, int d)
{
    int res = 0; 
    for(int i = 0; i <= n; i++)
        res = (1ll * res + (1ll * (i & 1 ? -1 : 1) * c[n][i] * fpow(2, 1ll * fpow(d, i, mod - 1) * fpow(d + 1, n - i, mod - 1) % (mod - 1), mod) % mod + mod) % mod) % mod; 
    return res; 
}

int main()
{
    T = read <int> ();
    for(int i = 0; i <= 1000; i++)
        for(int j = 0; j <= i; j++)
            c[i][j] = (!j ? 1 : (c[i - 1][j - 1] + c[i - 1][j]) % mod);
    while(T--)
    {
        n = read <int> (), d = read <int> ();
        printf("%d\n", (f(n, d) - f(n, d - 1) + mod) % mod); 
    }
    return 0; 
}

猜你喜欢

转载自www.cnblogs.com/ztlztl/p/12204215.html
今日推荐