专栏文章

P4609题解

P4609题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mioj9mdh
此快照首次捕获于
2025/12/02 20:07
3 个月前
此快照最后确认于
2025/12/02 20:07
3 个月前
查看原文

P4609 [FJOI2016] 建筑师

外训听完组合数学后题单里的,但觉得 dpdp 能过,写完真过了。结果一看提交记录,荣获最劣解。再一看题解,怎么都是斯特林数,蒟蒻不会,于是写篇题解

首先,由于要求前后缀最大值的个数,所以我们考虑将数字从大到小插入
dpi,j,kdp_{i,j,k} 为已经放了前 ii 大的数,前后缀最大值分别为 j,kj,k 的答案。
考虑将下一个数,即当前最大值插入对答案的影响。若放在两边,答案不变,前后缀最大值长度增加。若插在中间,由于已插入的数都比当前数大,不会对前后缀最大值产生影响,所以可以随便插,答案乘上 i2i-2
dpi,j,k=dpi1,j1,k+dpi1,j,k1+(i2)×dpi1,j,kdp_{i,j,k}=dp_{i-1,j-1,k}+dp_{i-1,j,k-1}+(i-2)\times dp_{i-1,j,k}
初值为 dp1,1,1=1dp_{1,1,1}=1
复杂度 O(nAB)O(nAB) 最大约 5×1085\times 10^8 但常数小,可以通过
但当你兴冲冲写完一交,满屏的 MLEMLE 发现空间也是 O(nAB)O(nAB) 的,所以考虑将第一维滚掉
所以我们先将询问离线,按照 nn 从小到大排序,然后一边跑 dpdp,一边记录答案
CPP
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10,M=1e9+7;
int n,A,B,dp[2][110][110],ans[N];
struct que{
	int n,a,b,id;
}q[N];
bool cmp(que x,que y){return x.n<y.n;}
signed main(){
	int T;
	cin>>T;
	for(int i=1;i<=T;i++){
		cin>>q[i].n>>q[i].a>>q[i].b;
		q[i].id=i;
	}
	sort(q+1,q+T+1,cmp);
	int l=1;
	while(q[l].n==1 and l<=T){ans[q[l].id]=((q[l].a==1)and(q[l].b==1));l++;}
	dp[0][1][2]=dp[0][2][1]=1;
	while(q[l].n==2 and l<=T){ans[q[l].id]=dp[0][q[l].a][q[l].b];l++;}
	for(int i=3;i<=q[T].n;i++){
		for(int j=1;j<=min(i,100);j++){
			for(int k=1;k<=min(i,100);k++){
				dp[i&1][j][k]=(dp[i&1^1][j-1][k]+dp[i&1^1][j][k-1]+1ll*dp[i&1^1][j][k]*(i-2))%M;	
			}
		}
		while(q[l].n==i and l<=T){ans[q[l].id]=dp[i&1][q[l].a][q[l].b];l++;}	
	}	
	for(int i=1;i<=T;i++)cout<<ans[i]<<'\n';
	return 0;
}
注意 nn 只有 5×1045\times 10^4,但 TT2×1052\times 10^5,不要开小数组

评论

0 条评论,欢迎与作者交流。

正在加载评论...