n n n个范围 [ l i , r i ] , a i ∈ [ l i , r i ] [l_i,r_i],a_i\in[l_i,r_i] [li,ri],ai∈[li,ri],求所有符合限制的单调不增序列的元素和的总和。
因为是a是单调不降的,所以他们区间的左端点应该是单调不降的,右端点是单调不增的。可以分区间讨论。
2n个点划分出2n个区间,然后用 d p ( i , j ) dp(i,j) dp(i,j)表示考虑了前i个区间,选择了j个数字的符合条件的序列的元素和。用 f ( i , j ) f(i,j) f(i,j)表示前i个区间选了j个数字的方案数,那么枚举有多少个数在当前区间范围内即可转移。
怎么转移呢?
区间有x个数字,可重复选出y个数字组成单调不增序列的方案数,可以转换为有x个桶,放y个小球的方案数。那么用隔板法我们知道方案数为 C ( x + y − 1 , x − 1 ) C(x+y-1, x-1) C(x+y−1,x−1).
这样前面的总和要乘上这么多的方案数。然后再看当前新加的数字的贡献:因为是所有方案的总和,所以可以取平均,在 [ l , r ] [l,r] [l,r]选一个数字相当于贡献 ( r + l ) / 2 (r+l)/2 (r+l)/2。那么选 y y y个对 d p ( i , j ) dp(i,j) dp(i,j)的额外贡献为:
单 个 数 字 贡 献 ∗ 数 字 个 数 ∗ 前 面 区 间 选 取 的 方 案 数 ∗ 当 前 区 间 可 以 选 取 的 方 案 数 单个数字贡献*数字个数* 前面区间选取的方案数 * 当前区间可以选取的方案数 单个数字贡献∗数字个数∗前面区间选取的方案数∗当前区间可以选取的方案数
其中单个数字贡献为 ( r + l ) / 2 (r+l)/2 (r+l)/2
数字个数为 y y y
前面区间选取方案为 f ( i − 1 , j − y ) f(i-1,j-y) f(i−1,j−y)
当前区间选取方案数用隔板法算一下
这样就得到了 d p ( i , j ) dp(i,j) dp(i,j)的转移。
f ( i , j ) f(i,j) f(i,j)的转移就是 当 前 区 间 方 案 数 ∗ 之 前 区 间 方 案 数 当前区间方案数*之前区间方案数 当前区间方案数∗之前区间方案数 的累加了。
具体看代码吧:
细节:
#include
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define mid ((l+r)>>1)
#define lson rt<<1, l, mid
#define rson rt<<1|1, mid+1, r
using namespace std;
/*注意爆long long*/
const int maxn = 205;
ll cc[200];
int num = 0;
int n;
ll l[55], r[55];
const ll mod = 998244353;
ll dp[200][200];
ll f[200][200];
ll fac[maxn], ifac[maxn], inv[maxn];
ll qm(ll a, ll b){ll res = 1; while(b) {if(b&1) res=res*a%mod; a = a*a%mod; b>>=1;} return res;}
ll Choose(ll a, ll b){//a个可选的数字,b个要放的数 C(b+a-1, b)
ll res = 1;
for(ll i = a; i < b+a; ++i) res = i%mod*res%mod;
for(ll i = 2; i <= b; ++i) res = res*inv[i]%mod;
return res;
}
int main()
{
fac[0] = ifac[0] = 1;
for(int i = 1; i < maxn; ++i) fac[i] = fac[i-1]*i%mod, ifac[i] = qm(fac[i], mod-2), inv[i] = qm(i, mod-2);
cin>>n;
for(int i = 1; i <= n; ++i){
scanf("%lld", &l[i]); l[i] = max(l[i], l[i-1]);
cc[++num] = l[i];
//cout<<"L:"<
}
for(int i = 1; i <= n; ++i){
scanf("%lld", &r[i]);
//cc[++num] = r[i]+1;
//cout<<"R:"<
}
cc[++num] = r[n]+1;
for(int i = n-1; i >= 1; --i){
r[i] = min(r[i], r[i+1]);
cc[++num] = r[i]+1;
}
sort(cc+1,cc+1+num);
num = unique(cc+1,cc+1+num)-cc-1;
assert(num < 200);
ll inv2 = (mod+1)/2;
f[0][0] = 1;
for(int i = 1; i < num; ++i){
dp[i][0] = 0;
f[i][0] = 1;
ll L = cc[i], R = cc[i+1]-1;
//cout<<"L:"<
for(int j = 1; j <= n; ++j){
dp[i][j] = dp[i-1][j];
f[i][j] = f[i-1][j];
for(int k = j; k > 0; --k){
if(l[k] <= L && R <= r[k]){
//cout<<"i:"<
dp[i][j] = (dp[i][j] +
dp[i-1][k-1]*Choose(R-L+1, j-k+1)%mod +
f[i-1][k-1]*Choose(R-L+1, j-k+1)%mod*(R%mod+L%mod)%mod*inv2%mod*(j-k+1)%mod )%mod;
f[i][j] = (f[i][j] + f[i-1][k-1]*Choose(R-L+1, j-k+1)%mod)%mod;
//cout<<"dp:"<
}else break;
}
// cout<<"i:"<
}
}
//cout<
ll ans = dp[num-1][n];
//assert(ans > 0);
cout<<ans<<endl;
}
/*
2
1 2
3 4
*/