局部递推

局部递推

Problem A. 部分错排问题

对满足以下条件的 排列 \(A\) 计数:

  1. 长度为 $n+m $;
  2. 对于任意在 $[1,m] $ 中的整数 \(i\)\(A_i\ne i\)

也就是排列中有 \(n\) 个自由元素,\(m\) 个限制元素。

我们称这样的排列 \(A\) 的个数为 部分错排数,记为 \(B_{n,m}\)

Version I

给出一组 \(n,m\)\(B_{n,m}\),其中 \(0\leq n,m\leq 10^6\)

根据容斥原理,答案即为

\[B_{n,m}=\sum_{k=0}^m \binom{m}{k} (n+m-k)! (-1)^{k} \]

Version II

对所有 \(n,m\)\(B_{n,m}\),其中 \(0\leq n,m \leq 2000\)

首先有 \(B_{n,0}=n!,B_{0,m}=D_m\)

对于其他情况,考虑 \(A_{n+m}\) 填什么。记 \(A_{n+m}=k\),分情况讨论:

  1. \(1\leq k\leq m\)。此时 \(A_k\ne k\) 这个限制不再生效,那么 \(A_k\) 填什么数字不再有限制。则在前 \(n+m-1\) 个位置中,还有 \(m-1\) 个位置有限制,\(n\) 个位置没限制,贡献为 \(m\times B_{n,m-1}\)
  2. \(m<k\leq m+n\)。这对于所有的 $ A_i\ne i$ 的限制没有任何影响。则在前 \(n+m-1\) 个位置中,还有 \(m\) 个位置有限制,\(n-1\) 个位置没限制,贡献为 \(n\times B_{n-1,m}\)

于是得出递推式 1:

\[B_{n,m}=\begin{cases}n! &m=0\\D_m &n=0\\m\times B_{n,m-1}+n\times B_{n-1,m} &otherwise\end{cases} \]

还有一种考虑方式:

  1. \(k=n+m\)。显然贡献为 \(B_{n-1,m}\)
  2. \(k\neq n+m\)。此时增加了一个 \(A_{n+m}\ne n+m\) 的限制,那么 \(n+m\) 变得有限制,贡献为 \(B_{n-1,m+1}\)

得到递推式 2:\(B_{n,m}=B_{n-1,m}+B_{n-1,m+1}\)

Version III

\(n\) 固定,对所有的 $ m$ 求 \(B_{n,m}\),其中 \(1\leq n \leq 10^7,1\leq m \leq 10^5\)

首先有 \(B_{n,0}=n!\)\(B_{n,1}=n\times n!\)

\(m\geq 2\) 时,考虑 \(A_m\) 填什么。记 \(A_m=k\),分情况讨论:

  1. \(1\leq k \leq m\)。类似于 \(D_n\) 的递推式 I,贡献为 \((m-1)(B_{n,m-1}+B_{n,m-2})\)
  2. \(m<k\leq m+n\)。此时 \(A_m\) 这个位置没有限制,那么还剩下 \(m-1\) 个位置有限制,贡献为 \(n\times B_{n,m-1}\)

于是得出递推式 3:

\[B_{n,m}=\begin{cases}n! &m=0\\n\times n! &m=1\\(n+m-1)B_{n,m-1}+(m-1)B_{n,m-2} &otherwise\end{cases} \]

Version IV

\(Q\) 组询问,每次询问一个 \(B_{n,m}\) 的值,\(1\leq n,m,Q\leq 10^5\)

假如我们已经知道 \(B_{n,m},B_{n,m+1}\) 的值。

由递推式 3,可以直接求出 \(B_{n,m-1},B_{n,m+2}\)

\[B_{n,m+2}=(m+1)B_{n,m}+(n+m+1)B_{n,m+1} \]

\[B_{n,m-1}=\dfrac{B_{n,m+1}-(n+m)B_{n,m}}{m} \]

由递推式 1,

\[B_{n-1,m+1}=\dfrac{B_{n,m+1}-(m+1)B_{n,m}}{n} \]

由递推式 2,

\[B_{n+1,m}=B_{n,m}+B_{n,m+1} \]

联立递推式 1,2,

\[\begin{aligned}B_{n+1,m+1}=(m+1)B_{n,m}+(n+m+2)B_{n,m+1}\end{aligned} \]

\[B_{n-1,m}=\dfrac{(n+m+1)B_{n,m}-B_{n,m+1}}{n} \]

我们可以 \(O(1)\) 求出 \((n,m),(n,m+1)\) 相邻 \(6\) 个位置的值,那么我们就可以 \(O(1)\) 地移动 \(n\)\(m\)

将所有询问离线后莫队求解。复杂度 \(O(N\sqrt{Q})\),其中 \(N=\max(n_i,m_i)\)

这种 trick 也叫作 局部递推,即列出若干个线性无关方程组实现参数的微调。

Problem B. P8367 [LNOI2022] 盒

首先考虑对于固定的 \(b\) 怎么求答案。

我们对 \(a,b\) 做前缀和得到 \(A,B\),将贡献拆到每一个 \(i\) 上,答案即为 \(\sum_{i=1}^{n-1} w_i|A_i-B_i|\)

接下来考虑如何计数。

分两种情况:\(A_i\geq B_i\)\(A_i<B_i\)。两种情况是对称的,我们先考虑第一种。

对于每一个位置 \(i\),我们需要知道 \(A_i\leq B_i\) 的方案数,权且设为 \(f\);还需要知道所有这样 \(B_i\) 的和,权且即为 \(g\)

那么 \(i\) 位置的贡献就为 \(w_i(f\times a_i-g)\)

\(\sum A_i = m\)。我们把 \(B_i\) 刻画为从 \((0,0)\) 走到 \((n,m)\) 的折线,第一次触碰直线 \(x=i\) 时的 \(y\) 坐标即为 \(B_i\)

枚举 \(B_i=j\),对应的折线为 \((0,0)\rightarrow (i-1,j)\rightarrow (i,j)\rightarrow (n-1,m)\)

那么:

\[f=\sum_{j=0}^{a_i} \binom{i+j-1}{i-1} \binom{n+m-i-j-1}{n-i-1} \]

\[g=\sum_{j=0}^{a_i}j \binom{i+j-1}{i-1} \binom{n+m-i-j-1}{n-i-1} \]

\(g\) 里面乘的 \(j\) 看上去有些难受,所以我们展开二项式系数消掉它:

\[j\binom{i+j-1}{i-1}=j\dfrac{(i+j-1)!}{(i-1)!j!}=i\dfrac{(i+j-1)!}{i!(j-1)!}=i\binom{i+j-1}{i} \]

代入原式:

\[\begin{aligned} g&=i\sum_{j=0}^{a_i}\binom{i+j-1}{i} \binom{n+m-i-j-1}{n-i-1}\\ &= i\sum_{j=0}^{a_i-1}\binom{i+j}{i} \binom{n+m-i-j-2}{n-i-1}\\ &=i\sum_{j=0}^{a_i-1} \binom{(i+1)-j-1}{(i+1)-1}\binom{(n+1)+(m-1)-(i+1)-j-1}{(n+1)-(i+1)-1} \end{aligned} \]

发现等式右边和 \(f\) 形式是一样的,那么设:

\[f_{n,m,i,k}=\sum_\limits{j=0}^k \dbinom{i+j-1}{i-1} \dbinom{n+m-i-j-1}{n-i-1} \]

\[g_{n,m,i,k}=\sum_\limits{j=0}^k j\dbinom{i+j-1}{i-1} \dbinom{n+m-i-j-1}{n-i-1} \]

\(g_{n,m,i,k}=i\times f_{n+1,m-1,i+1,k-1}\)

接下来只需要处理 \(f\) 即可。

可以发现,\(i\) 增大的过程中,\(k\) 单调不降。如果可以快速实现 \(i,k\) 增大 \(1\),就可以在 \(O(n+m)\) 内解决问题。

显然有:

\[f_{n,m,i,k+1}=f_{n,m,i,k}+\binom{i+k}{i-1}\binom{n+m-i-k-2}{n-i-1} \]

\(i\) 增大时,回到 \(f\) 的组合意义: \((0,0)\rightarrow (i-1,j)\rightarrow (i,j)\rightarrow (n-1,m)\) 的折线数量,其中 \(0\leq j\leq k\)

那么此时增加了 \(B_{i+1}\leq k\) 这一条件,且原来合法的折线现在依然合法。

所以我们只要减去符合原来条件但不符合新条件的折线数即可。

可以发现,这样的折线必定会从 \((i,k)\) 走到 \((i,k+1)\),再任意地走到 \((n-1,m)\)

于是得到:

\[f_{n,m,i+1,k}=f_{n,m,i,k}-\binom{i+k}{i}\binom{n+m-i-k-2}{n-i-1} \]

下面来解决 \(A_i<B_i\) 的情况。

类似地,设:

\[h_{n,m,i,k}=\sum_\limits{j=k}^m \dbinom{i+j-1}{i-1} \dbinom{n+m-i-j-1}{n-i-1} \]

\[z_{n,m,i,k}=\sum_\limits{j=k}^m j\dbinom{i+j-1}{i-1} \dbinom{n+m-i-j-1}{n-i-1} \]

同理可得:

\[z_{n,m,i,k}=i\times h_{n+1,m-1,i+1,k-1} \]

\(k\) 增大时,显然有:

\[h_{n,m,i,k+1}=h_{n,m,i,k}-\binom{i+k-1}{i-1}\binom{n+m-i-k-1}{n-i-1} \]

\(i\) 增大时,相当于减少了 \(B_i\geq k\) 这一条件。所以只要减去原来不合法但现在合法的折线数。

可以发现,这样的折线一定从 \((i,k-1)\) 走到 \((i,k)\) 再走到 \((n-1,m)\),则有:

\[h_{n,m,i+1,k}=h_{n,m,i,k}+\binom{i+k-1}{i-1}\binom{n-m-i-k-1}{n-i-1} \]

所以,我们只需要当 \(i,k\) 移动时,维护出两个 \(f\) 和两个 \(h\) 的变化,就可以在 \(O(n+m)\) 内解决一组数据。

struct Value1{
	int vn,vm,vi,vk;
	ll res;
	inline void MoveK(){(res+=C(vi+vk,vi-1)*C(vn+vm-vi-vk-2,vn-vi-1))%=mod;vk++;}
	inline void MoveI(){(res+=mod-C(vi+vk,vi)*C(vn+vm-vi-vk-2,vn-vi-1)%mod)%=mod;vi++;}
}F1,F2;

struct Value2{
	int vn,vm,vi,vk;
	ll res;
	inline void MoveK(){(res+=mod-C(vi+vk-1,vi-1)*C(vn+vm-vi-vk-1,vn-vi-1)%mod)%=mod;vk++;}
	inline void MoveI(){(res+=C(vi+vk-1,vi)*C(vn+vm-vi-vk-1,vn-vi-1))%=mod;vi++;}
}G1,G2;

void Solve(){
	read(n);
	for(int i=1;i<=n;i++){
		read(a[i]);
		a[i]+=a[i-1];
	}
	m=a[n];
	for(int i=1;i<n;i++) read(w[i]);
	F1=Value1{n,m,1,0,C(n+m-2,n-2)};
	F2=Value1{n+1,m-1,1,-1,0};
	ll ans=0;
	for(int i=1;i<n;i++){
		while(F1.vi<i) F1.MoveI();
		while(F1.vk<a[i]) F1.MoveK();
		while(F2.vi<i+1) F2.MoveI();
		while(F2.vk<a[i]-1) F2.MoveK();
		(ans+=w[i]*(F1.res*a[i]%mod+mod-i*F2.res%mod))%=mod;
	}
	G1=Value2{n,m,1,0,0};
	G2=Value2{n+1,m-1,1,0,0};
	for(int i=0;i<=m;i++) (G1.res+=C(n+m-i-2,n-2))%=mod;
	for(int i=0;i<m;i++) (G2.res+=C(n+m-i-2,n-1))%=mod;
	for(int i=1;i<n;i++){
		while(G1.vk<a[i]+1) G1.MoveK();
		while(G1.vi<i) G1.MoveI();
		while(G2.vk<a[i]) G2.MoveK();
		while(G2.vi<i+1) G2.MoveI();
		(ans+=w[i]*(i*G2.res%mod+mod-a[i]*G1.res%mod))%=mod;
	}
	printf("%lld\n",ans);
}

signed main(){
	int T; read(T);
    Init();
	while(T--){
		Solve();
	}
    return 0;
}
posted @ 2025-04-25 18:53  XP3301_Pipi  阅读(39)  评论(0)    收藏  举报
Title