• @topcoder



    @description@

    现有不等式组:

    [egin{cases} x_1 + x_2 + dots + x_m leq s\ x_i leq t &1 leq i leq n end{cases} ]

    求该不等式组的正整数解个数。

    原题传送门。

    @accepted code@

    发现这道题就 (m - n leq 100) 可以利用,其他数据范围都太大了。

    如果 m = n,由于还有条件 (nt leq s),所以答案 = (n^t)

    如果 m = n + 1,假设前 n 个数之和为 p,则最后 1 个数对答案的贡献为 (s-p-1)。
    可以拆成 (s-1) 与 (-p),前者实际上就是 ((s-1) imes n^t);而后者如果考虑每一个变量的贡献为 (-x_i),总贡献即 (-p),推一下式子即可。

    进一步地,不妨猜想假如前 n 个数之和为 p,则后 n - m 个数对答案的贡献是关于 p 的 n - m 次多项式。

    事实上,后 n - m 个数的贡献就是 ({s - pchoose n - m})(比较显然)。由此也可以看出的确是关于 p 的 n - m 次多项式。
    我们暴力 (O((n - m)^2)) 展开该多项式得到 (sum_{i=0}^{n-m}a_ip^i)

    接下来如果算出 (f_i = sum_{1leq x_1, x_2, dots, x_nleq t}(x_1 + x_2 + dots + x_n)^i),最后答案就是 (ans = sum a_if_i)

    (F(x) = sum_{i=0}frac{sum_{j=1}^{t}j^i}{i!}x^i),则 (f_i = i! imes [x^i]F^n(x))

    自然数幂和(使用斯特林数即可)算出 (F(x)) 前 m - n 项,再快速幂算 (F^n(x))。多项式乘法直接 (O((m-n)^2)) 算。

    因此总时间复杂度 (O((m-n)^2log n))

    @accepted code@

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    typedef long long ll;
    
    const int MAXN = 105;
    const int MOD = 1000000007;
    
    inline int add(int x, int y) {return (x + y >= MOD ? x + y - MOD : x + y);}
    inline int sub(int x, int y) {return (x - y < 0 ? x - y + MOD : x - y);}
    inline int mul(int x, int y) {return 1LL * x * y % MOD;}
    
    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 fct[MAXN + 5], ifct[MAXN + 5], b[MAXN + 5];
    void init() {
    	fct[0] = 1; for(int i=1;i<=MAXN;i++) fct[i] = mul(fct[i - 1], i);
    	ifct[MAXN] = pow_mod(fct[MAXN], MOD - 2);
    	for(int i=MAXN-1;i>=0;i--) ifct[i] = mul(ifct[i + 1], i + 1);
    	
    	for(int i=0;i<=MAXN;i++) {
    		b[i] = (i == 0);
    		for(int j=0;j<i;j++)
    			b[i] = sub(b[i], mul(b[j], ifct[i-j+1]));
    	}
    }
    int sum(int n, int k) {
    	int ret = 0; n = add(n, 1);
    	for(int i=1,pw=n;i<=k+1;i++,pw=mul(pw,n))
    		ret = add(ret, mul(b[k-i+1], mul(pw, ifct[i])));
    	if( k == 0 ) ret = sub(ret, 1);
    	return mul(ret, fct[k]);
    }
    
    int a[MAXN + 5];
    void solve(int s, int p) {
    	a[0] = 1;
    	for(int i=0;i<p;i++) {
    		int k = sub(s, i);
    		a[i + 1] = sub(0, a[i]);
    		for(int j=i;j>=1;j--)
    			a[j] = sub(mul(k, a[j]), a[j - 1]);
    		a[0] = mul(k, a[0]);
    	}
    	for(int i=0;i<=p;i++)
    		a[i] = mul(a[i], ifct[p]);
    }
    
    int t1[MAXN + 5], t2[MAXN + 5];
    void mul(int *A, int *B, int p) {
    	for(int i=0;i<=p;i++) t1[i] = A[i], t2[i] = B[i], A[i] = 0;
    	for(int i=0;i<=p;i++)
    		for(int j=0;j<=i;j++)
    			A[i] = add(A[i], mul(t1[j], t2[i-j]));
    }
    
    int f[MAXN + 5], g[MAXN + 5];
    void solve2(int p, ll n, int t) {
    	for(int i=0;i<=p;i++)
    		f[i] = 0, g[i] = mul(sum(t, i), ifct[i]);
    	f[0] = 1;
    	
    	for(ll i=n;i;i>>=1,mul(g,g,p))
    		if( i & 1 ) mul(f, g, p);
    }
    
    class TrickyInequality{
    	public:
    		int countSolutions(ll s, int t, int n, int m) {
    			int p = m - n, ans = 0;
    			init(), solve(s % MOD, p), solve2(p, n, t);
    			for(int i=0;i<=p;i++)
    				ans = add(ans, mul(a[i], mul(f[i], fct[i])));
    			return ans;
    		}
    };
    

    @details@

    另外,这题好像可以用直接容斥然后构造 k 阶差分公式做到 (O((n-m)^2))(orz 栋爷)。

    有空。。。再来填坑?

  • 相关阅读:
    随机验证码实验报告
    输出验证
    原码,反码,补码!
    枚举验证结论
    Java第一次考试有感
    暑假第七周学习进度报告
    暑假第六周学习进度报告
    MySQL学习笔记(22)——常用的日期时间函数
    execute()返回值——true or false
    ORM
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/12625456.html
Copyright © 2020-2023  润新知