• @gym



    @description@

    给定一个大小为 (l_1 imes l_2 dots l_d) 的 d 维超矩形,将它的一个角放置在原点,使得它的第 i 维在范围 ([0, l_i])

    求出这个矩形中满足 (x_1 + x_2 + dots + x_d leq s) 的点形成的体积 × d!。

    原题传送门。

    @solution@

    首先,简单积分一下可以得到满足 (sum_{i=1}^{d}x_i leq s) 的点形成的体积为 (frac{s^d}{d!})

    update in 2020/07/09:草我是哈批,做到这一步直接容斥 (x_ileq l_i) 的限制就行了。也不需要下面那些乱七八糟的东西。


    因为数据都是整数,我们不妨去考虑每一个 (1 imes 1 dots imes 1) 的单位超矩形 ((x_1, x_2, dots x_d) - (x_1 + 1, x_2 + 1, dots x_d + 1)) 的贡献。

    除去完全包含((s - sum_{i=1}^{d}x_i geq d))与完全不包含((s - sum_{i=1}^{d}x_i leq 0)),还有 d - 1 种不完全包含的情况 ((0 < s - sum_{i=1}^{d}x_i < d))。

    可以作个背包 + 简单前缀和优化算出每一种情况有多少个单位超矩形。

    然而不完全包含的情况并不能直接通过积分算体积。不妨仍考虑组合方法,记 (f_j) 表示满足 (s - sum_{i=1}^{d}x_i = j) 的体积。

    对于 (sum_{i=1}^{d}x_i leq s),积分出来的体积是 (frac{s^d}{d!});然而我们也可以采用单位超矩形的贡献算出体积。
    不妨只考虑 s < d 的情况,则可以列出等式 (frac{s^d}{d!} = sum_{i=0}^{s}{s - i + d - 1choose d - 1}f_i),由此就可以在 (f_s)(f_{0...s-1}) 之间建立递推关系。

    时间复杂度瓶颈在背包的部分,为 O(l^3)。

    @accepted code@

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 300;
    const int MAXS = MAXN*MAXN;
    const int MOD = int(1E9) + 7;
    
    inline int add(int x, int y) {return (x + y) % MOD;}
    inline int mul(int x, int y) {return 1LL*x*y % MOD;}
    inline int sub(int x, int y) {return add(x, MOD-y);}
    
    int pow_mod(int b, int p) {
    	int ret = 1;
    	for(int i=p;i;i>>=1,b=mul(b,b))
    		if( i & 1 ) ret = mul(ret, b);
    	return ret;
    }
    
    int l[MAXN + 5], d, s, S;
    
    int fct[2*MAXN + 5], ifct[2*MAXN + 5];
    int comb(int n, int m) {
    	return mul(fct[n], mul(ifct[m], ifct[n-m]));
    }
    int f[MAXN + 5];
    void init() {
    	fct[0] = 1;
    	for(int i=1;i<=2*d;i++)
    		fct[i] = mul(fct[i-1], i);
    	ifct[2*d] = pow_mod(fct[2*d], MOD-2);
    	for(int i=2*d-1;i>=0;i--)
    		ifct[i] = mul(ifct[i+1], i+1);
    	for(int i=1;i<=d;i++) {
    //		f[i] = mul(pow_mod(i, d), ifct[d]);
    		f[i] = pow_mod(i, d);
    		for(int j=0;j<i;j++)
    			f[i] = sub(f[i], mul(comb(i-j+d-1, d-1), f[j]));
    	}
    }
    
    int g[MAXS + 5];
    int main() {
    	scanf("%d", &d), init();
    	for(int i=1;i<=d;i++)
    		scanf("%d", &l[i]), l[i]--;
    	scanf("%d", &s);
    	g[0] = 1;
    	for(int i=1;i<=d;i++) {
    		S += l[i];
    		for(int j=1;j<=S;j++) g[j] = add(g[j], g[j-1]);
    		for(int j=S;j>l[i];j--) g[j] = sub(g[j], g[j-l[i]-1]);
    	}
    	int ans = 0;
    	for(int i=0;i<=s;i++)
    		ans = add(ans, mul(f[min(s-i,d)], g[i]));
    	printf("%d
    ", ans);
    }
    

    @details@

    一开始总以为它是一道积分题,结果发现怎么积分都不对劲。

    然后尝试不从积分走而从组合数学走,发现还真能做出来。

  • 相关阅读:
    Serverless 工程实践 | Serverless 应用开发观念的转变
    如何高效学习 Kubernetes 知识图谱?
    互动赠新书|当云原生遇到混合云:如何实现“求变”与“求稳”的平衡
    5 款阿里常用代码检测工具,免费用!
    AI与传统编译器
    OpenArkCompiler方舟编译
    传统编译原理
    LLVM基础技术图例
    双极型与低频大功率晶体管
    TVM,Relay,Pass
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12409385.html
Copyright © 2020-2023  润新知