题解:[PA 2020] Malowanie płotu

题目传送门

绝世前缀和优化 DP 好题。

题意分析

即找出长度为 \(n\) 的序列 \(\langle[l_1,r_1],[l_2,r_2],\cdots,[l_n,r_n]\rangle\) 的个数,且满足:

  • \(l_i,r_i\) 为整数,且 \(1\leq l_i\leq r_i\leq m\)
  • 对于 \(1\leq i<n\),有 \([l_i,r_i],[l_{i+1},r_{i+1}]\) 相交。

朴素 DP

\(\textit{dp}_{i,l,r}\) 为第 \(i\) 项为 \([l,r]\) 的方案数。

枚举第 \(i-1\)\([l_2,r_2]\),有:

\[\textit{dp}_{i,l,r}=\sum_{l_2=1}^m\sum_{r_2=l_2}^{m}[[l,r]\cap[l_2,r_2]\neq\varnothing]\textit{dp}_{i-1,l_2,r_2} \]

答案即:

\[\sum_{l=1}^m\sum_{r=l}^m\textit{dp}_{n,l,r} \]

朴素转移 \(\mathcal O\left(nm^4\right)\)

前缀和优化 DP

发现,与区间 \([l,r]\) 有交的 \([l_2,r_2]\) 可以用所有合法区间减去无交区间得到。

因此可以有:

\[\begin{aligned} \textit{dp}_{i,l,r}&=\sum_{l_2=1}^m\sum_{r_2=l_2}^{m}[[l,r]\cap[l_2,r_2]\neq\varnothing]\textit{dp}_{i-1,l_2,r_2}\\ &=\sum_{l_2=1}^m\sum_{r_2=l_2}^m\textit{dp}_{i-1,l_2,r_2}-\sum_{l_2=r+1}^m\sum_{r_2=l_2}^m\textit{dp}_{i-1,l_2,r_2}-\sum_{r_2=l-1}^m\sum_{l=1}^{r_2}\textit{dp}_{i-1,l_2,r_2} \end{aligned} \]

显然可以维护 \(\textit{dp}_{i-1,l,r}\) 的所有状态的和 \(\textit{sum}_i\),即:

\[\textit{sum}_i=\sum_{l=1}^m\sum_{r=l}^m\textit{dp}_{i,l,r} \]

故有:

\[\textit{dp}_{i,l,r}=\textit{sum}_{i-1}-\sum_{l_2=r+1}^m\sum_{r_2=l_2}^m\textit{dp}_{i-1,l_2,r_2}-\sum_{r_2=l-1}^m\sum_{l=1}^{r_2}\textit{dp}_{i-1,l_2,r_2} \]

考虑优化后面的部分。

对于 \(\displaystyle\sum_{l=x+1}^m\sum_{r=l}^m\textit{dp}_{i-1,l,r}\),即 \(x\) 右边的所有 DP 值之和。显然可以使用后缀和优化。

记:

\[\begin{aligned} \displaystyle\textit{suf}_{i,x}&=\textit{suf}_{i+1,x}+\sum_{r=x}^m\textit{dp}_{i,x,r}\\ \displaystyle\textit{pre}_{i,x}&=\textit{pre}_{i-1,x}+\sum_{l=1}^x\textit{dp}_{i,l,x} \end{aligned} \]

分别表示 \(x\) 及其右边的所有区间的方案数和 \(x\) 及其左边的所有区间的方案数。

这显然可以在求解 DP 时求出来。

因此可以有:

\[\textit{dp}_{i,l,r}=\textit{sum}_{i-1}-\textit{suf}_{i-1,r+1}-\textit{pre}_{i-1,l-1} \]

发现,此时答案即 \(\textit{suf}_{n,1}\)。又可以发现,此时状态 \(\textit{dp}_{i,l,r}\) 已经没有意义

\(\textit{sum}_i\) 可以进一步优化:

\[\begin{aligned} \textit{sum}_i&=\sum_{l=1}^m\sum_{r=l}^m\textit{dp}_{i,l,r}\\ &=\sum_{l=1}^m\sum_{r=l}^m\left(\textit{sum}_{i-1}-\textit{suf}_{i-1,r+1}-\textit{pre}_{i-1,l-1}\right)\\ &=\sum_{l=1}^m\left((m-l+1)(\textit{sum}_{i-1}-\textit{pre}_{i-1,l-1})-\sum_{r=l}^{m}\textit{suf}_{i-1,r+1}\right) \end{aligned} \]

这启发我们可以维护 \(\displaystyle\sum_{r=l}^{m}\textit{suf}_{i-1,r+1}\) 的后缀和来优化。因此记:

\[\begin{aligned} \textit{sufSumSuf}_{i,x}&=\sum_{l=x}^m\textit{suf}_{i,x}\\ &=\textit{sufSumSuf}_{i,x+1}+\textit{suf}_{i,x}\\ \textit{preSumPre}_{i,x}&=\sum_{r=x}^m\textit{pre}_{i,x}\\ &=\textit{preSumPre}_{i,x+1}+\textit{pre}_{i,x}\\ \end{aligned} \]

则可以将 \(\textit{sum}_i\) 的递推转化为:

\[\textit{sum}_i=\sum_{l=1}^m\left((m-l+1)(\textit{sum}_{i-1}-\textit{pre}_{i-1,l-1})-\textit{sufSumSuf}_{l+1}\right) \]

同样地,代入可以更改 \(\textit{pre}_{i,x},\textit{suf}_{i,x}\) 的递推式,最终与 \(\textit{dp}_{i,l,r}\) 无关。有:

\[\begin{aligned} \textit{pre}_{i,x}&=\textit{pre}_{i,x-1}+x\left(\textit{sum}_{i-1}-\textit{suf}_{i-1,x+1}\right)-\textit{preSumPre}_{i-1,x-1}\\ \textit{suf}_{i,x}&=\textit{suf}_{i,x+1}+(m-x+1)\left(\textit{sum}_{i-1}-\textit{pre}_{i-1,x-1}\right)-\textit{preSumPre}_{i-1,x+1}\\ \end{aligned} \]

此时状态量为 \(\mathcal O(nm)\),转移 \(\mathcal O(1)\)

时间复杂度:\(\mathcal O(nm)\)。由于 \(1\leq nm\leq10^7\),可以通过。


使用滚动数组,而不要使用 vectorvector。会被卡空间。

AC 代码

//#include<bits/stdc++.h>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<cstdio>
#include<string>
#include<vector>
#include<cmath>
#include<ctime>
#include<deque>
#include<queue>
#include<stack>
#include<list>
using namespace std;
constexpr const int N=1e7,M=1e7;
int n,m,P,ans;
int sum[2],pre[2][N+1],preSumPre[2][N+1],suf[2][N+1],sufSumSuf[2][N+1];
int main(){
	/*freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);*/
	
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n>>m>>P;
	bool op=0;
	for(int l=1;l<=m;l++){
		suf[op][l]=m-l+1;
	}
	for(int r=1;r<=m;r++){
		pre[op][r]=r;
	}
	sum[op]=(m*(m+1ll)>>1)%P;
	for(int r=1;r<=m;r++){
		pre[op][r]=(pre[op][r]+pre[op][r-1])%P;
		preSumPre[op][r]=(pre[op][r]+preSumPre[op][r-1])%P;
	}
	sufSumSuf[op][m]=suf[op][m];
	for(int l=m-1;1<=l;l--){
		suf[op][l]=(suf[op][l+1]+suf[op][l])%P;
		sufSumSuf[op][l]=(suf[op][l]+sufSumSuf[op][l+1])%P;
	}
	for(int i=2;i<=n;i++){
		op=!op;
		sum[op]=0;
		for(int r=1;r<=m;r++){
			pre[op][r]=(1ll*r*(sum[!op]-suf[!op][r+1])-preSumPre[!op][r-1])%P;
		}
		for(int l=1;l<=m;l++){
			suf[op][l]=((m-l+1ll)*(sum[!op]-pre[!op][l-1])-sufSumSuf[!op][l+1])%P;
		}
		for(int l=1;l<=m;l++){
			sum[op]=(sum[op]+(m-l+1ll)*(sum[!op]-pre[!op][l-1])%P-sufSumSuf[!op][l+1])%P;
		}
		for(int r=1;r<=m;r++){
			pre[op][r]=(pre[op][r]+pre[op][r-1])%P;
			preSumPre[op][r]=(pre[op][r]+preSumPre[op][r-1])%P;
		}
		sufSumSuf[op][m]=suf[op][m];
		for(int l=m-1;1<=l;l--){
			suf[op][l]=(suf[op][l+1]+suf[op][l])%P;
			sufSumSuf[op][l]=(suf[op][l]+sufSumSuf[op][l+1])%P;
		}
	}
	int ans=suf[op][1];
	if(ans<0){
		ans+=P;
	}
	cout<<ans<<'\n';
	
	cout.flush();
	
	/*fclose(stdin);
	fclose(stdout);*/
	return 0;
}
posted @ 2025-08-22 13:56  TH911  阅读(6)  评论(0)    收藏  举报