[题解] [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;
}