P9108 [PA2020] Malowanie płotu

rgw2010發表於2024-09-03

題意:

給定 \(n,m\),一個區間序列 \(\{[L_1,R_1],[L_2,R_2],\cdots,[L_n,R_n]\}\) 被稱為好的當且僅當:

  • \(\forall i \in [1,n],1 \le L_i \le R_i \le m\)

  • \(\forall i \in [1,n-1],[L_i,R_i] \cup [L_{i+1},R_{i+1}] \ne \emptyset\)

輸出好的序列個數對給定質數 \(p\) 取模的結果

\(nm \le 10^7\)

思路:

考慮動態規劃演算法,定義 \(dp_{i,l,r}\) 表示第 \(i\) 次為 \([l,r]\),則狀態轉移方程為:

\[dp_{i,l,r} = \sum_{L=1}^r \sum_{R=\max(L,l)}^m dp_{L,R} \]

這種是不好轉移的,考慮做一個容斥,用總的減去與 \(L \le R < l\)\(r< L \le R\) 的貢獻,記 \(s_i\) 表示所有 \(dp_{i,l,r}\) 的和:

\[dp_{i,l,r} = s_{i-1} - \sum_{L=1}^{l-1} \sum_{R=L}^{l-1} dp_{L,R} - \sum_{L=r+1}^m \sum_{R=L}^m dp_{L,R} \]

我們可以記 \(f_{i,j}\) 表示所有滿足 \(r \le j\)\(dp_{i,l,r}\) 的和,記 \(g_{i,j}\) 滿足所有 \(j \le l\)\(dp_{i,l,r}\) 的和,則狀態轉移方程更新為:

\[dp_{i,l,r} = f_{i-1,m} - f_{i-1,l-1} - g_{i-1,r+1} \]

時間複雜度透過字首和最佳化至 \(O(nm^2)\),考慮繼續最佳化。

考慮如何快速求 \(f_{i,j}\)\(g_{i,j}\),可以直接爆推:

\[\begin{aligned} f_{i,j} &= \sum_{l=1}^j \sum_{r=l}^j dp_{i,l,r} \\ &= \sum_{l=1}^j \sum_{r=l}^j f_{i-1,m} - f_{i-1,l-1} - g_{i-1,r+1} \\ & = \frac{j(j+1)}{2} f_{i-1,m} - \Big( \sum_{l=1}^j \sum_{r=l}^j f_{i-1,l-1} + g_{i-1,r+1} \Big) \\ &= \frac{j(j+1)}{2} f_{i-1,m} - \sum_{l=1}^j (j-l+1) \times f_{i-1,l-1} - \sum_{r=1}^j r \times g_{i-1,r+1} \\ &= \frac{j(j+1)}{2} f_{i-1,m} - \sum_{r=1}^j r \times g_{i-1,r+1} - \Big( \sum_{l=1}^j j \times f_{i-1,l-1} - \sum_{l=1}^j (l-1) \times f_{i-1,l-1} \Big) \\ &= \frac{j(j+1)}{2} f_{i-1,m} - \sum_{r=1}^j r \times g_{i-1,r+1} - j\sum_{l=1}^j f_{i-1,l-1} + \sum_{l=1}^j (l-1) \times f_{i-1,l-1} \end{aligned} \]

\[\begin{aligned} g_{i,j} &= \sum_{l=j}^m \sum_{r=l}^m dp_{i,l,r} \\ &= \sum_{l=j}^m \sum_{r=l}^m f_{i-1,m} - f_{i-1,l-1} - g_{i-1,r+1} \\ &= \frac{(m+j)(m-j+1)}{2} f_{i-1,m} - \Big(\sum_{l=j}^m \sum_{r=l}^m f_{i-1,l-1} + g_{i-1,r+1} \Big) \\ &= \frac{(m+j)(m-j+1)}{2} f_{i-1,m} - \sum_{l=j}^m (m-l+1) f_{i-1,l-1}-\sum_{r=j}^m (r-j + 1) g_{i-1,r+1} \\ & = \frac{(m+j)(m-j+1)}{2} f_{i-1,m} - \sum_{l=j}^m (m-l+1) f_{i-1,l-1} - \sum_{r=j}^m r \times g_{i-1,r+1} + (j-1) \sum_{r=j}^m g_{i-1,r+1} \end{aligned} \]

這樣時間複雜度最佳化為 \(O(nm)\)

完整程式碼:

#include<bits/stdc++.h>
#define Add(x,y) (x+y>=mod)?(x+y-mod):(x+y)
#define lowbit(x) x&(-x)
#define pi pair<ll,ll>
#define pii pair<ll,pair<ll,ll>>
#define iip pair<pair<ll,ll>,ll>
#define ppii pair<pair<ll,ll>,pair<ll,ll>>
#define fi first
#define se second
#define full(l,r,x) for(auto it=l;it!=r;it++) (*it)=x
#define Full(a) memset(a,0,sizeof(a))
#define open(s1,s2) freopen(s1,"r",stdin),freopen(s2,"w",stdout);
#define For(i,l,r) for(register int i=l;i<=r;i++)
#define _For(i,l,r) for(register int i=r;i>=l;i--)
using namespace std;
typedef double db;
typedef unsigned long long ull;
typedef long long ll;
bool Begin;
const int N=1e7+10; 
inline int read(){
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-')
          f=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x*f;
}
inline void write(int x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9)
	  write(x/10);
	putchar(x%10+'0');
}
int n,m,st,s1,s2,s3,s4,mod;
int f[2][N],g[2][N];
bool End;
int main(){
	n=read(),m=read(),mod=read();
	For(i,1,m)
	  f[0][i]=(f[0][i-1]+i)%mod;
	_For(i,1,m)
	  g[0][i]=(g[0][i+1]+m-i+1)%mod;
	For(i,2,n){
		st^=1;
		For(j,0,m+1)
		  f[st][j]=g[st][j]=0; 
		s1=s2=s3=s4=0;
		For(j,1,m){
			s1=(s1+j)%mod;
			s2=(s2+1ll*g[st^1][j+1]*j%mod)%mod;
			s3=(s3+f[st^1][j-1])%mod;
			s4=(s4+1ll*f[st^1][j-1]*(j-1)%mod)%mod;
			f[st][j]=(1ll*s1*f[st^1][m]%mod-s2-1ll*s3*j%mod+s4+mod*2ll)%mod;
		}
		s1=s2=s3=s4=0;
		_For(j,1,m){
			s1=(s1+m-j+1)%mod;
			s2=(s2+1ll*f[st^1][j-1]*(m-j+1)%mod)%mod;
			s3=(s3+1ll*g[st^1][j+1]*j%mod)%mod;
			s4=(s4+g[st^1][j+1])%mod;
			g[st][j]=(1ll*s1*f[st^1][m]%mod-s2-s3+1ll*s4*(j-1)%mod+mod*2)%mod;
		}
	}
	write(f[st][m]);
	cerr<<'\n'<<abs(&Begin-&End)/1048576<<"MB";
	return 0;
}