## AT_dp_j
dp$_{i,j,k}$表示剩i个一个寿司的,j个两个寿司的,k个三个寿司的概率
$$dp_{i, j, k} = \dfrac{n - i -j - k}{n} \times (dp_{i, j, k} + 1) + \dfrac{i}{n} \times (dp_{i - 1, j, k} + 1) + \dfrac{j}{n} \times (dp_{i + 1, j - 1, k} + 1) + \dfrac{k}{n} \times (dp_{i, j + 1, k - 1} + 1) = \dfrac{n - i - j - k}{n} \times dp_{i, j, k} + \dfrac{i}{n} \times dp_{i - 1, j, k} + \dfrac{j}{n} \times dp_{i + 1, j - 1, k} + \dfrac{k}{n} \times dp_{i, j + 1, k - 1} + 1$$
移项。
$$\dfrac{i + j + k}{n} \times dp_{i, j, k} = \dfrac{i}{n} \times dp_{i - 1, j, k} + \dfrac{j}{n} \times dp_{i + 1, j - 1, k} + \dfrac{k}{n} \times dp_{i, j + 1, k - 1} + 1$$
整理得转移方程。
$$dp_{i, j, k} = \dfrac{i}{i + j + k} \times dp_{i - 1, j, k} + \dfrac{j}{i + j + k} \times dp_{i + 1, j - 1, k} + \dfrac{k}{i + j + k} \times dp_{i, j + 1, k - 1} + \dfrac{n}{i + j + k}$$
```cpp
#include<bits/stdc++.h>
using namespace std;
double f[305][305][305];
int a,b,c,n;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
int x;
cin>>x;
if(x==1){
a++;
}else if(x==2){
b++;
}else{
c++;
}
}
for(int k=0;k<=n;k++){
for(int j=0;j<=n;j++){
for(int i=0;i<=n;i++){
if(i==0&&j==0&&k==0) continue;
f[i][j][k]=1.0*n/(i+j+k);
if(i!=0) f[i][j][k]+=1.0*(i)/(i+j+k)*f[i-1][j][k];
if(j!=0) f[i][j][k]+=1.0*(j)/(i+j+k)*f[i+1][j-1][k];
if(k!=0) f[i][j][k]+=1.0*(k)/(i+j+k)*f[i][j+1][k-1];
}
}
}
printf("%.10lf",f[a][b][c]);
return 0;
}
```
---
## AT_dp_o
$dp[i][j]$(1<=$i$<=n)(0<=$j$<(1<<$n$-1))表示选取前i个男生对应匹配的女生集合为j时的方案数
```cpp
#include<bits/stdc++.h>
using namespace std;
long long f[2][5000005],mod=1000000007;
int n,e[25][25],p;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>e[i][j];
}
}
f[p][0]=1;
for(int i=1;i<=n;i++){
for(int j=0;j<(1<<(n));j++){
int cnt=__builtin_popcount(j);
if(cnt!=i) continue;
for(int k=0;k<n;k++){
if(e[i][k+1]&&((1<<k)&j)!=0){
f[p^1][j]=(f[p^1][j]+f[p][j^(1<<k)])%mod;
}
}
}
p=1-p;
}
cout<<f[p][(1<<n)-1];
return 0;
}
```
---
## AT_dp_r
$f[t][i][j]$为$i$到$j$长度为$t$的路径的方案数
$f[t][i][j]=\sum_{i=1}^{n}f[t-x][i][k]*f[x][k][j]$
发现x取任何值都会得到相同的答案,所以x取1时
$f[t][i][j]=\sum_{i=1}^{n}f[t-1][i][k]*f[1][k][j]$
=$\sum_{i=1}^{n}f[t-1][i][k]*a[k][j]$
发现该式子和矩乘一样,可以愉快的矩乘加速
```cpp
#include<bits/stdc++.h>
using namespace std;
int n;
long long a[55][55],b[55][55],c[55][55],k,mod=1000000007;
void ch(long long x[][55],long long y[][55]){
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) c[i][j]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
c[i][j]=(c[i][j]+x[i][k]*y[k][j])%mod;
}
}
}
}
void qpow(long long y){
while(y){
if(y%2==1){
ch(b,a);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) b[i][j]=c[i][j];
}
ch(a,a);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]=c[i][j];
y/=2;
}
}
int main(){
cin>>n>>k;
for(int i=1;i<=n;i++){
b[i][i]=1;
for(int j=1;j<=n;j++){
cin>>a[i][j];
}
}
qpow(k);
long long ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
ans=(ans+b[i][j])%mod;
}
}
cout<<ans;
return 0;
}
```
---
## [AT_dp_z](https://www.luogu.com.cn/problem/AT_dp_z)
斜率优化
$dp[i]$表示到第$i$格的最短费用
不难写出转移方程:
$dp[i]=\sum_{j=1}^{j=i-1}dp[j]+(h[i]-h[j])^2+C$
O(n^2)可以求出
考虑化简
$dp[i]=dp[j]+h[i]^2+h[j]^2+C-2*h[i]*h[j]$
对于$j<k$要使从$j$转移更优,需满足
$dp[j]+h[i]^2+h[j]^2+C-2*h[i]*h[j]<dp[k]+h[i]^2+h[k]^2+C-2*h[i]*h[k]$
化简得
$dp[j]+h[j]^2-2*h[i]*h[j]<dp[k]+h[k]^2-2*h[k]*h[k]$
$-2*h[i]*(h[j]-h[k])<dp[k]+h[k]^2-h[j]^2-dp[j]$
$h[i]\geq\frac{dp[j]+h[j]^2-h[k]^2-dp[k]}{2*(h[j]-h[k])}$
此时不等式的左边类似于求斜率的公式,故称为斜率优化。
---
对于优先队列里的$i<j<k$
$j$是优的,当且仅当$slope(i,j)\geq h[i]且slope(i,j)\leq h[i]$
既$slope(i,j)\geq slope(i,j)$
```cpp
#include<bits/stdc++.h>
using namespace std;
long long n,c,h[200005],q[200005],head=1,tail=1,f[200005];
double slope(int x,int y){
return (double)(h[x]*h[x]-h[y]*h[y]+f[x]-f[y])/((double)(h[x]-h[y])*2);
}
int main(){
cin>>n>>c;
for(int i=1;i<=n;i++){
cin>>h[i];
}
q[1]=1;
f[1]=0;
for(int i=2;i<=n;i++){
while(head<tail&&slope(q[head+1],q[head])<=h[i]) head++;
f[i]=f[q[head]]+(h[i]-h[q[head]])*(h[i]-h[q[head]])+c;
while(head<tail&&slope(q[tail],q[tail-1])>=slope(i,q[tail])) tail--;
q[++tail]=i;
}
cout<<f[n];
return 0;
}
```
4万+

被折叠的 条评论
为什么被折叠?



