题解:P16433 [APIO 2026 中国赛区] 上升
前言
哎哎哎,送上 Ag 的题。别问我为什么不会 T2,问就是因为纯菜。
事后更新:有 T1 的加持,还是拿不下 Ag 吗。
感谢 Register_int 大哥在 APIO 前几天在 LA 里带我补习组合计数 /bx
思路
将所有
由于题目不保证模数
对于一个位置
-
如果
p_i=0 ,那么枚举i 被涉及到的极长连续上升段[i',i] ,其中c\le i'\le i (强行在c 左侧打隔断),其中有一位置k 使得i'-1\le k\le i ,在k 及之前的元素<v 而在k 之后的元素>v 。这样会使j 增加k-i'+1 ,方案数为f_{i'-1,j} 乘剩余v-j 个值有顺序地选k-i'+1 个值以及把往i'-1-j 个>v 的值里插入i-k 个已知大小顺序的值的方案数,再乘\prod_{i'\le c<i}w_c 。直接枚举会爆,所以我们对这个断点k 再开一个 dp 数组处理。 -
如果
p_i\not=0 ,那么设上一个非0 在位置c' ,值是v' 。枚举i 被涉及到的极长连续上升段[i',i] ,其中c’\le i'\le i 。- 如果
i'>c' ,由于我们拓展了v ,所以考虑f_{i'-1,j} 的贡献:对于未选中的i'-1-j 个>v' 的元素,枚举数量m ,从(v',v) 中有顺序地选择m 个值分配给钦定的顺序中的前m 小,使得j 增加m 。对于新的j ,我们从v-1-j 个值中选择i-i'-1 个,然后将j 增加i-i'-1 。 - 如果
i'=c' ,由于左端点右端点都是固定的,那么考虑f_{c',j} 的贡献:先从(v',v) 中有顺序地选择i-c'-1 个元素分配给两个位置中间的所有位置,然后枚举m ,从新增的(v',v) 除去i-c'-1 个值中有顺序地选择m 个值分配给钦定顺序中的前m 小,将j 增加i-c'-1+m 。 - 上文略去了
w 的贡献。最后更新c,v 。由于每个i' 在这一步都只贡献了至多两次O(n^2) 的转移,所以复杂度是对的。
- 如果
然后我们就成功绕开除法操作在
如果上面有疑似出错的地方或者看不懂的地方,可以看代码配合理解。
代码
代码等 CCF 发。不发我就再写一份。
代码重写了,使用了好朋友 AVENGER_M 的码风 /mgx
#include<bits/stdc++.h>
#define lglg long long
using namespace std;
int cid,n;
lglg modp,wei[505],iwei[505][505],C[505][505],f[505][505],g[505][505];
int ascend(int CI,int N,int M,vector<int>P,vector<int>W){
cid=CI;n=N;modp=M;for(int i=1;i<n;i++)wei[i]=(W[i-1]+modp-1)%modp;
for(int i=0;i<=n;i++){
C[i][0]=1;for(int j=1;j<=i;j++)C[i][j]=(C[i-1][j-1]+C[i-1][j])%modp;
}
for(int i=1;i<=n;i++){
iwei[i][i-1]=1;for(int j=i;j<n;j++)iwei[i][j]=iwei[i][j-1]*wei[j]%modp;
}
f[0][0]=1;int val=0,lst=0;
for(int i=1;i<=n;i++){
if(P[i-1]==0){
for(int j=lst;j<i;j++)for(int k=0;k<=val;k++){
f[i][k]=(f[i][k]+f[j][k]*iwei[j+1][i-1]%modp*C[i-k][i-j])%modp;
if(k+i-j<=val)g[i][k+i-j]=(g[i][k+i-j]+f[j][k]*iwei[j+1][i-1]%modp*C[val-k][i-j])%modp;
}
for(int j=lst;j<=i;j++)for(int k=0;k<=val;k++){
f[i][k]=(f[i][k]+g[j][k]*iwei[j][i-1]%modp*C[i-k][i-j])%modp;
}
}
else{
int v=P[i-1];
for(int j=lst;j<i;j++)for(int k=0;k<=val;k++)for(int kk=k;kk<v;kk++){
if(kk+i-j<=v)f[i][kk+i-j]=(f[i][kk+i-j]+f[j][k]*iwei[j+1][i-1]%modp*C[v-val-1][kk-k]%modp*C[v-kk-1][i-j-1])%modp;
}
if(lst&&v-val>=i-lst)for(int k=0;k<=val;k++)for(int kk=k;kk<v;kk++){
if(kk+i-lst<=v)f[i][kk+i-lst]=(f[i][kk+i-lst]+f[lst][k]*iwei[lst][i-1]%modp*C[v-val-i+lst][kk-k]%modp*C[v-val-1][i-lst-1])%modp;
}
for(int j=0;j<=v;j++)g[i][j]=f[i][j];
lst=i;val=v;
}
}
int ans=f[n][val];memset(C,0,sizeof(C));memset(f,0,sizeof(f));memset(g,0,sizeof(g));
return ans;
}