1 条题解
-
0
没有已知数是好做的。我们考虑插入法 DP,每次将新数插入到原先的值域连续段中的一个位置,设计 DP 表示前 个位置已完成并第 个位置的相对值为 的价值。
考虑有已知数怎么做。已知数是递增的。如果沿用插入法做法的话,每个已知数相当于给值域上各个前缀设定了一个个数限制。如果我们摊开整个值域的话,已知数将值域分成了若干个可填入的连续段,但我们并不能维护所有连续段的信息。
然而我们事实上只关心序列上相邻两数的大小关系。依旧考虑维护待填连续段,但是每当得到一个确定的值时,我们就把待填连续段上比他小的前缀一块填入到具体值域序列里面去,也就是贡献延后。记最近的已知数是 ,在下一个已知数之前,将加入的数分两种情况讨论:比 小的直接填入值域序列,否则加入待填连续段。
所以只需要 表示考虑了前 个数, 之前还有 个空位,最后一个数的相对值为 的价值。待填的数个数为 。 的具体含义是, 时表示上一步填入了第 个 前的空, 时表示上一步的数在待定段中是第 个,这样设计下标连续且转移相似。
那么现在我们得到了一个 的做法。然后前缀和一下做完了。
int ascend(int c,int n,int m,vec<int> P,vec<int> W){ const int N=505; mod=m;FMODINIT(m); sto int p[N];sto mint w[N]; rep(i,1,n)p[i]=P[i-1]; repl(i,1,n)w[i]=W[i-1];w[0]=w[n]=1; sto mint C[N][N]; rep(i,0,n){ C[i][0]=1; rep(j,1,i)C[i][j]=C[i-1][j]+C[i-1][j-1]; } sto mint f[2][N][N<<1],s[N<<1]; int lst=0,dt=0;f[0][0][0]=1;p[n+1]=n+1; rep(i,1,n+1){ dt^=1;MSET(f[dt],0),MSET(s,0); if(p[i]){ rep(j,max(0,lst-(i-1)),lst){ s[0]=f[dt^1][j][0];repl(k,1,j+i-lst+j)s[k]=s[k-1]+f[dt^1][j][k]; rep(l,1,min(i-lst+j,p[i]-lst)){ f[dt][j+p[i]-lst-l][j+p[i]-lst-l]+=(s[j+l-1]*w[i-1]+s[j+i-lst+j-1]-s[j+l-1])*C[p[i]-lst-1][l-1]; // repl(k,0,j+l)f[dt][j+p[i]-lst-l][j+p[i]-lst-l]+=f[dt^1][j][k]*w[i-1]*C[p[i]-lst-1][l-1]; // repl(k,j+l,j+i-lst+j)f[dt][j+p[i]-lst-l][j+p[i]-lst-l]+=f[dt^1][j][k]*C[p[i]-lst-1][l-1]; } } lst=p[i]; }else{ rep(j,max(0,lst-(i-1)),lst){ s[0]=f[dt^1][j][0];repl(k,1,j+i-lst+j)s[k]=s[k-1]+f[dt^1][j][k]; rep(v,1,j){ f[dt][j-1][v-1]+=s[v-1]*w[i-1]+s[j+i-lst+j-1]-s[v-1]; // repl(k,0,v)f[dt][j-1][v-1]+=f[dt^1][j][k]*w[i-1]; // repl(k,v,j+i-lst+j)f[dt][j-1][v-1]+=f[dt^1][j][k]; } rep(v,j+1,j+i-lst+j){ f[dt][j][v]+=s[v-1]*w[i-1]+s[j+i-lst+j-1]-s[v-1]; // repl(k,0,v)f[dt][j][v]+=f[dt^1][j][k]*w[i-1]; // repl(k,v,j+i-lst+j)f[dt][j][v]+=f[dt^1][j][k]; } } } } return f[dt][0][0].x; }
- 1
信息
- ID
- 12582
- 时间
- 3500ms
- 内存
- 1100MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者