题面
题解
由于会出现重复的情况, 我们考虑如何去掉这种情况
我们可以这样考虑, 若每一维的最小值都为 (0) , 那么我们将这一种情况算作合法情况
同理, 若某几维的最小值不为 (0) , 我们必定可以经过平移将它的这一维移至 (0) , 所以这一种情况我们不算进去
这样就可以不重不漏的算出有多少个集合了, 并且该点集中每个点每一维都 (in [0, d])
考虑容斥计算总方案数, 设 (f(d)) 代表直径 (leq d) 的点集有多少个
[displaystyle f(d) = sum_{i = 0}^{n}(-1)^iinom{n}{i}2^{d^i(d+1)^{n-i}}
]
我们考虑枚举有至少 (i) 维差的最大值 (< d) , 从 (n) 各种选出 (i) 个, 有 (inom{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
", (f(n, d) - f(n, d - 1) + mod) % mod);
}
return 0;
}