【LNOI2022】盒(组合数学)

题面

n n n 个不同的盒子排成一排。在初始状态下,第 i i i 个盒子放有 a i a_i ai 个货物,总货物数量为 S = ∑ i = 1 n a i S = \sum_{i = 1}^{n} a_i S=i=1nai。对于非负整数数组 ( b 1 , b 2 , … , b n ) (b_1, b_2, \ldots, b_n) (b1,b2,,bn) 满足 ∑ i = 1 n b i = S \sum_{i = 1}^{n} b_i = S i=1nbi=S,考察以下问题:

你想让第 i i i 个盒子中拥有恰好 b i b_i bi 个货物,为此你可以做如下操作若干次:对两个相邻的盒子,把其中一个盒子的恰好一个货物移动至另一个。对 i , i + 1 i, i + 1 i,i+1 1 ≤ i < n 1 \le i < n 1i<n)号盒子做一次操作的代价为 w i w_i wi注意:将 i \bm{i} i 号盒子的一个货物移动到 i + 1 \bm{i + 1} i+1 号盒子和将 i + 1 \bm{i + 1} i+1 号盒子的一个货物移动到 i \bm{i} i 号盒子的代价都是 w i \bm{w_i} wi。你需要保证在操作中不存在盒子中的货物数量是负数。

在以上问题下,定义从初始状态达到第 i i i 个盒子拥有恰好 b i b_i bi 个货物的状态的最小代价为 val ⁡ ( b 1 , b 2 , … , b n ) \operatorname{val}(b_1, b_2, \ldots, b_n) val(b1,b2,,bn),你需要求出对所有满足 ∑ i = 1 n b i = S \sum_{i = 1}^{n} b_i = S i=1nbi=S 的非负整数数组 ( b 1 , b 2 , … , b n ) (b_1, b_2, \ldots, b_n) (b1,b2,,bn) val ⁡ ( b 1 , b 2 , … , b n ) \operatorname{val}(b_1, b_2, \ldots, b_n) val(b1,b2,,bn) 的和。输出这个答案对 998244353 998244353 998244353 取模后的结果。

输入格式

本题有多组测试数据。输入的第一行包含一个正整数 T T T,表示测试数据组数。

对于每组数据,输入共三行。第一行一个正整数 n n n 表示盒子的个数,第二行 n n n 个正整数 a 1 , a 2 , … , a n a_1, a_2, \ldots, a_n a1,a2,,an 描述初始状态,第三行 n − 1 n - 1 n1 个正整数 w 1 , w 2 , … , w n − 1 w_1, w_2, \ldots, w_{n - 1} w1,w2,,wn1 描述移动货物的代价。

输出格式

对于每组测试数据输出一行一个整数,表示对于所有满足 ∑ i = 1 n b i = S \sum_{i = 1}^{n} b_i = S i=1nbi=S 的非负整数数组,达到目标的代价的最小值之和模 998244353 998244353 998244353 的结果。

样例 #1

样例输入 #1

2
2
2 3
65472
5
1 3 2 1 1
2 3 3 3

样例输出 #1

589248
8589

提示

【样例解释 #1】

对于第一组数据,一共有六种需要考虑的情形,它们的 b 1 b_1 b1 b 2 b_2 b2 构成的二元组 ( b 1 , b 2 ) (b_1, b_2) (b1,b2) 分别为 ( 0 , 5 ) , ( 1 , 4 ) , ( 2 , 3 ) , ( 3 , 2 ) , ( 4 , 1 ) , ( 5 , 0 ) (0, 5), (1, 4), (2, 3), (3, 2), (4, 1), (5, 0) (0,5),(1,4),(2,3),(3,2),(4,1),(5,0)

对于第一种情形,需要至少 2 2 2 次移动,代价最小值为 65472 × 2 = 130944 65472 \times 2 = 130944 65472×2=130944

对于第二种情形,需要至少 1 1 1 次移动,代价最小值为 65472 65472 65472

对于第三种情形,不需要做任何操作,代价最小值为 0 0 0

对于第四种情形,需要至少 1 1 1 次移动,代价最小值为 65472 65472 65472

对于第五种情形,需要至少 2 2 2 次移动,代价最小值为 65472 × 2 = 130944 65472 \times 2 = 130944 65472×2=130944

对于最后一种情形,需要至少 3 3 3 次移动,代价最小值为 65472 × 3 = 196416 65472 \times 3 = 196416 65472×3=196416

因此,最小代价之和为 130944 + 65472 + 0 + 65472 + 130944 + 196416 = 589248 130944 + 65472 + 0 + 65472 + 130944 + 196416 = 589248 130944+65472+0+65472+130944+196416=589248

【数据范围】

保证对于任何测试点的任何一组数据,有 2 ≤ n ≤ 5 × 10 5 2 \le n \le 5 \times {10}^5 2n5×105 1 ≤ S ≤ 2 × 10 6 1 \le S \le 2 \times {10}^6 1S2×106 a i ≥ 0 a_i \ge 0 ai0 0 ≤ w i < 998244353 0 \le w_i < 998244353 0wi<998244353

测试点编号 T ≤ T \le T n ≤ n \le n S ≤ S \le S特殊性质
1 ∼ 2 1 \sim 2 12 1000 1000 1000 5 5 5 5 5 5A
3 ∼ 4 3 \sim 4 34 5 5 5 9 9 9 9 9 9
5 ∼ 8 5 \sim 8 58 10 10 10 2000 2000 2000 2000 2000 2000
9 ∼ 12 9 \sim 12 912 10 10 10 2000 2000 2000 2 × 10 5 2 \times {10}^5 2×105
13 ∼ 14 13 \sim 14 1314 2 2 2 2 × 10 5 2 \times {10}^5 2×105 2 × 10 5 2 \times {10}^5 2×105B
15 ∼ 16 15 \sim 16 1516 2 2 2 2 × 10 5 2 \times {10}^5 2×105 2 × 10 5 2 \times {10}^5 2×105AC
17 ∼ 18 17 \sim 18 1718 2 2 2 2 × 10 5 2 \times {10}^5 2×105 2 × 10 5 2 \times {10}^5 2×105
19 ∼ 20 19 \sim 20 1920 5 5 5 5 × 10 5 5 \times {10}^5 5×105 2 × 10 6 2 \times {10}^6 2×106

特殊性质 A:对于任意 1 ≤ i < n 1 \le i < n 1i<n w i = 1 w_i = 1 wi=1
特殊性质 B:对于任意 1 ≤ i < n − 20 1 \le i < n - 20 1i<n20 a i = 0 a_i = 0 ai=0
特殊性质 C:最多只有 20 20 20 i ∈ [ 1 , n ] i \in [1, n] i[1,n] 满足 a i ≠ 0 a_i \ne 0 ai=0

【提示】

本题有读入量较大的测试点,为了优化程序运行的时间,我们建议你采用较为快速的读入方式。

题解

【声明】本人是看过 OID 的讲解才做出来的

我们对于每个缝单独算贡献,在贪心的移动下,容易知道该缝的经过次数为左边 b b b 求和与 a a a 求和的差值。

a i a_i ai 的前缀和为 A i A_i Ai ,我们要求的就是
∑ k = 1 n − 1 w k ∑ i = 0 S ∣ A k − i ∣ ( i + k − 1 k − 1 ) ( S − i + n − k − 1 n − k − 1 ) \sum_{k=1}^{n-1}w_k\sum_{i=0}^S|A_k-i|{i+k-1\choose k-1}{S-i+n-k-1\choose n-k-1} k=1n1wki=0SAki(k1i+k1)(nk1Si+nk1)

把绝对值拆开,变成条件,
∑ k = 1 n − 1 w k ∑ i = 0 A k ( A k − i ) ( i + k − 1 k − 1 ) ( S − i + n − k − 1 n − k − 1 ) \sum_{k=1}^{n-1}w_k\sum_{i=0}^{A_k}(A_k-i){i+k-1\choose k-1}{S-i+n-k-1\choose n-k-1} k=1n1wki=0Ak(Aki)(k1i+k1)(nk1Si+nk1)

正反各求一遍就是答案。这时我们把 A k − i A_k-i Aki 拆开来算贡献,已知
( i + k − 1 k − 1 ) i = ( i + k − 1 k ) k \bm{{i+k-1\choose k-1}i={i+k-1\choose k}k} (k1i+k1)i=(ki+k1)k

故上式为
∑ k = 1 n − 1 w k A k ∑ i = 0 A k ( i + k − 1 k − 1 ) ( S − i + n − k − 1 n − k − 1 ) − ∑ k = 1 n − 1 w k k ∑ i = 0 A k ( i + k − 1 k ) ( S − i + n − k − 1 n − k − 1 ) \sum_{k=1}^{n-1}w_kA_k\sum_{i=0}^{A_k}{i+k-1\choose k-1}{S-i+n-k-1\choose n-k-1}\\ -\sum_{k=1}^{n-1}w_kk\sum_{i=0}^{A_k}{i+k-1\choose k}{S-i+n-k-1\choose n-k-1} k=1n1wkAki=0Ak(k1i+k1)(nk1Si+nk1)k=1n1wkki=0Ak(ki+k1)(nk1Si+nk1)

两个结构相似,我们只考察上面一个,令
f ( k , L ) = ∑ i = 0 L    − k ( i + k − 1 k − 1 ) ( S − i + n − k − 1 n − k − 1 ) = ∑ i = 0 L − 1 ( i k − 1 ) ( S − i + n − 2 n − k − 1 )   = ∑ i = 0 L − 1    ( i k − 1 ) ( 1 1 ) ( S − i + n − 2 n − k − 1 ) f(k,L)=\sum_{i=0}^\bm{L~~-k}{i+k-1\choose k-1}{S-i+n-k-1\choose n-k-1}\\ =\sum_{i=0}^\bm{L-1}{i\choose k-1}{S-i+n-2\choose n-k-1}\\~\\ \bm{=\sum_{i=0}^{L-1}~~{i\choose k-1}{1\choose1}{S-i+n-2\choose n-k-1}} f(k,L)=i=0L  k(k1i+k1)(nk1Si+nk1)=i=0L1(k1i)(nk1Si+n2) =i=0L1  (k1i)(11)(nk1Si+n2)

转化成最后一个式子应该比较清晰了,我们考察它的组合意义,它其实就是 S + n − 1 S+n-1 S+n1 个位置中选 n − 1 n-1 n1 个,满足 k \bm{k} k 个的位置 ≤ L \bm{\leq L} L 的方案数

f ( k , L ) f(k,L) f(k,L) 是可以递推的,
f ( k + 1 , L ) = f ( k , L ) − ( L k ) ( S + n − 1 − L n − 1 − k ) f ( k , L + 1 ) = f ( k , L ) + ( L k − 1 ) ( S + n − 2 − L n − 1 − k ) f(k+1,L)=f(k,L)-{L\choose k}{S+n-1-L\choose n-1-k}\\ f(k,L+1)=f(k,L)+{L\choose k-1}{S+n-2-L\choose n-1-k} f(k+1,L)=f(k,L)(kL)(n1kS+n1L)f(k,L+1)=f(k,L)+(k1L)(n1kS+n2L)

由于我们每次需要的是 f ( i , A i + k ) f(i,A_i+k) f(i,Ai+k) ,而 i i i A i + k A_i+k Ai+k 都是递增的,所以均摊 O ( 1 ) O(1) O(1)

总时间复杂度 O ( n + S ) O(n+S) O(n+S)

CODE

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<unordered_map>
//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast")
using namespace std;
#define MAXN 2500005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define PR pair<int,int>
int xchar() {
	static const int maxn = 1000000;
	static char b[maxn];
	static int pos = 0,len = 0;
	if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);
	if(pos == len) return -1;
	return b[pos ++];
}
// #define getchar() xchar()
LL read() {
	LL f = 1,x = 0;int s = getchar();
	while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}
	while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}
	return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {
	if(!x) {putchar('0');return ;}
	if(x<0) putchar('-'),x = -x;
	return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}

const int MOD = 998244353;
int n,m,s,o,k;
inline int Abs(int x) {return x<0 ? -x:x;}
inline void MD(int &x) {if(x>=MOD)x-=MOD;}
int a[MAXN],w[MAXN];
int fac[MAXN],inv[MAXN],invf[MAXN];
inline int C(int n,int m) {
    if(m < 0 || m > n) return 0;
    return fac[n] *1ll* invf[n-m] % MOD * invf[m] % MOD;
}
int main() {
	// freopen("box.in","r",stdin);
    // freopen("box.out","w",stdout);
    fac[0]=fac[1]=inv[0]=inv[1]=invf[0]=invf[1]=1;
    for(int i = 2;i <= MAXN-5;i ++) {
        fac[i] = fac[i-1] *1ll* i % MOD;
        inv[i] = (MOD - inv[MOD%i]) *1ll* (MOD/i) % MOD;
        invf[i] = invf[i-1] *1ll* inv[i] % MOD;
    }
    int T = read();
    while(T --) {
        n = read();
        int S = 0;
        for(int i = 1;i <= n;i ++) S += (a[i] = read());
        for(int i = 1;i < n;i ++) w[i] = read();
        int L = 0,ans = 0,L2 = 0;
        int nm = C(S+n-1,n-1),lm = 0;
        int nm2 = C(S+n-2,n-1),lm2 = 1;
        for(int i = 1;i < n;i ++) {
            L += a[i]+1; int p = i; L2 += a[i];
            MD(nm += MOD- C(lm,p-1)*1ll*C(S+n-1-lm,n-p)%MOD);
            MD(nm2 += MOD- C(lm2,p)*1ll*C(S+n-1-lm2,n-p)%MOD);
            while(lm < L) {
                lm ++;
                MD(nm += C(lm-1,p-1)*1ll*C(S+n-1-lm,n-1-p)%MOD);
            }
            while(lm2 < L) {
                lm2 ++;
                MD(nm2 += C(lm2-1,p)*1ll*C(S+n-1-lm2,n-1-p)%MOD);
            }
            int as = (nm*1ll*w[i]%MOD*L2%MOD +MOD- nm2*1ll*w[i]%MOD*p%MOD)%MOD;
            MD(ans += as);
        }
        L = 0; nm = C(S+n-1,n-1); lm = 0; L2 = 0;
        nm2 = C(S+n-2,n-1); lm2 = 1;
        for(int i = n-1;i > 0;i --) {
            L += a[i+1]+1; int p = n-i; L2 += a[i+1];
            MD(nm += MOD- C(lm,p-1)*1ll*C(S+n-1-lm,n-p)%MOD);
            MD(nm2 += MOD- C(lm2,p)*1ll*C(S+n-1-lm2,n-p)%MOD);
            while(lm < L) {
                lm ++;
                MD(nm += C(lm-1,p-1)*1ll*C(S+n-1-lm,n-1-p)%MOD);
            }
            while(lm2 < L) {
                lm2 ++;
                MD(nm2 += C(lm2-1,p)*1ll*C(S+n-1-lm2,n-1-p)%MOD);
            }
            MD(ans += nm*1ll*w[i]%MOD*L2%MOD);
            MD(ans += MOD- nm2*1ll*w[i]%MOD*p%MOD);
        }
        AIput(ans,'\n');
    }
	return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值