題意:
給定 \(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;
}