插入类DP
对于这类题目的特征:
1.排列性质
2.
3.答案和排列的上升下降的突变点有关
套路:
按照某个从小到大的顺序插入,有了这个顺序就能算贡献或者方案数
贡献往往提前计算,存在每个元素有新开一段、合并两端、连在某一段后面的不同方案
有的允许了
新开一个段和连在某个段的边上并非同一个状态,新开一个段,未来会在两端之间插入更大的数字来合并两个 段,连在一个段的边上,则未来就不会如此
关于端点:
1.如果固定了端点,往往是当
2.如果没有固定左右端点,往往多加一维记录当前确定了几个端点(如果左右端点的转移/贡献相同),或者多加两维记录当前确定了左/右端点(如果左右端点的转移/贡献并不相同)
QY太强了,令我的总结旋转
按照结尾数字排名进行的插入类
A. 【Atcoder_DP】T-排列 (AI)
有一个长度为
对于任意满足
考虑
显然可以列出递推式
用前缀和优化,就可以把时间复杂度优化到
#include"bits/stdc++.h"
using namespace std;
constexpr int N=3015,P=1e9+7;
#define inl inline
#define regi register
#define PII pair<int,int>
#define mkp(x,y) make_pair(x,y)
//#define ll long long
//char buf_[1<<20],*_now=buf_,*_end=buf_;
//#define getchar() (_now==_end&&(_end=(_now=buf_)+fread(buf_,1,1<<20,stdin),_now==_end)?EOF:*_now++)
//namespace IO{void Unbind(){std::ios::sync_with_stdio(false);std::cin.tie(0);}}
//inl int add_(int a,int b,int p=P){return a+b>=p?a+b-p:a+b;}
//inl int sub_(int a,int b,int p=P){return a-b<0?a-b+p:a-b;}
//inl int mul_(int a,int b,int p=P){return 1ll*a*b%p;}
inl int read(void)
{
int x=0;short f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) f=ch=='-'?-1:f;
for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
return x*f;
}
int f[N][N],sum[N][N];
int n;int ans;
char ch[N];
int main(void)
{
n=read();
cin>>ch+1;
for(int i=1;i<=n;i++) sum[1][i]=i;
for(int i=2;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
if(ch[i-1]=='<') f[i][j]=sum[i-1][j-1]%P;
else f[i][j]=(sum[i-1][i-1]-sum[i-1][j-1]+P)%P;
sum[i][j]=(sum[i][j-1]+f[i][j])%P;
}
}
printf("%d ",sum[n][n]);
return 0;
}
B. F - Deforestation (AI)
有
每次可以选择一个
考虑限制,对于
- 先拿
再拿 ,收益是 - 先拿
再拿 , 收益是
显然,对于更大的那个,先拿是最优的,如果相等就随意,
于是类似于
#include"bits/stdc++.h"
using namespace std;
constexpr int N=5015,P=1e9+7;
#define inl inline
#define regi register
#define PII pair<int,int>
#define mkp(x,y) make_pair(x,y)
//#define ll long long
//char buf_[1<<20],*_now=buf_,*_end=buf_;
//#define getchar() (_now==_end&&(_end=(_now=buf_)+fread(buf_,1,1<<20,stdin),_now==_end)?EOF:*_now++)
//namespace IO{void Unbind(){std::ios::sync_with_stdio(false);std::cin.tie(0);}}
//inl int add_(int a,int b,int p=P){return a+b>=p?a+b-p:a+b;}
//inl int sub_(int a,int b,int p=P){return a-b<0?a-b+p:a-b;}
//inl int mul_(int a,int b,int p=P){return 1ll*a*b%p;}
inl int read(void)
{
int x=0;short f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) f=ch=='-'?-1:f;
for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
return x*f;
}
int f[N][N],sum[N][N];
int n,a[N];
int main(void)
{
n = read();
for (int i = 1;i <= n;i ++) a[i] = read();
f[1][1] = 1;
for (int i = 1;i <= n;i ++) sum[1][i] = 1;
for (int i = 2; i <= n;i ++)
{
for (int j = 1;j <= i;j ++)
{
if (a[i] < a[i - 1]) f[i][j] = sum[i - 1][j - 1]; else
if (a[i] > a[i - 1]) f[i][j] = (sum[i - 1][i - 1] - sum[i - 1][j - 1] + P) % P; else
if (a[i] == a[i - 1]) f[i][j] = sum[i - 1][i - 1];
sum[i][j] = (sum[i][j - 1] + f[i][j]) % P;
}
}
printf ("%d ",sum[n][n]);
return 0;
}
C. kangaroo
袋鼠,有
问从
两个路线不同,当且仅当草丛被访问的顺序不同,保证至少有一种方案初始时可以往任意方向跳
用
对于处理
考虑需要处理
#include"bits/stdc++.h"
using namespace std;
constexpr int N=2e3+15,P=1e9+7;
#define inl inline
#define regi register
#define PII pair<int,int>
#define mkp(x,y) make_pair(x,y)
#define ll long long
//char buf_[1<<20],*_now=buf_,*_end=buf_;
//#define getchar() (_now==_end&&(_end=(_now=buf_)+fread(buf_,1,1<<20,stdin),_now==_end)?EOF:*_now++)
//namespace IO{void Unbind(){std::ios::sync_with_stdio(false);std::cin.tie(0);}}
//inl int add_(int a,int b,int p=P){return a+b>=p?a+b-p:a+b;}
//inl int sub_(int a,int b,int p=P){return a-b<0?a-b+p:a-b;}
//inl int mul_(int a,int b,int p=P){return 1ll*a*b%p;}
inl int read(void)
{
int x=0;short f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) f=ch=='-'?-1:f;
for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
return x*f;
}
int n,s,t;
ll f[N][N];
int main(void)
{
n = read(),s = read(),t = read();
f[1][1] = 1;
for(int i = 2;i <= n;i ++)
{
for(int j = 1;j <= i;j ++)
{
if(i != s && i != t) f[i][j] = (j * f[i - 1][j + 1] % P +
(j - (i > s) - (i > t)) * f[i - 1][j - 1] % P) % P;
else f[i][j] = f[i - 1][j - 1] + f[i - 1][j];
}
}
printf("%lld ",f[n][1]);
return 0;
}
D.CF704B Ant Man
有
- 若
, 则 - 若
, 则
用
普通的情况
对于起点
新开一个段,
对于终点
新开一个段,
考虑起点和终点固定下来,对转移有什么影响
想要新开一段时,当
想往某个段的前面放,要么
想往某个段的后面放,要么
#include"bits/stdc++.h"
using namespace std;
constexpr int N=5e3+15;
#define inl inline
#define regi register
#define PII pair<int,int>
#define mkp(x,y) make_pair(x,y)
#define ll long long
//char buf_[1<<20],*_now=buf_,*_end=buf_;
//#define getchar() (_now==_end&&(_end=(_now=buf_)+fread(buf_,1,1<<20,stdin),_now==_end)?EOF:*_now++)
//namespace IO{void Unbind(){std::ios::sync_with_stdio(false);std::cin.tie(0);}}
//inl int add_(int a,int b,int p=P){return a+b>=p?a+b-p:a+b;}
//inl int sub_(int a,int b,int p=P){return a-b<0?a-b+p:a-b;}
//inl int mul_(int a,int b,int p=P){return 1ll*a*b%p;}
inl int read(void)
{
int x=0;short f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) f=ch=='-'?-1:f;
for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
return x*f;
}
ll f[N][N];
int n,s,e;int x[N],a[N],b[N],c[N],d[N];
inl void cmin(ll &a,ll b){return a=min(a,b),void();}
int main(void)
{
n=read(),s=read(),e=read();
for(int i=1;i<=n;i++) x[i]=read();for(int i=1;i<=n;i++) a[i]=read();for(int i=1;i<=n;i++) b[i]=read();for(int i=1;i<=n;i++) c[i]=read();for(int i=1;i<=n;i++) d[i]=read();
memset(f,0x3f,sizeof f);
f[0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
if(i==e) cmin(f[i][j],min(f[i-1][j]+x[i]+a[i],f[i-1][j-1]-x[i]+b[i])); else
if(i==s) cmin(f[i][j],min(f[i-1][j-1]-x[i]+d[i],f[i-1][j]+x[i]+c[i])); else
{
if(j>(i>s)+(i>e)) cmin(f[i][j],f[i-1][j-1]-2*x[i]+b[i]+d[i]);
if(j>1||i<s) cmin(f[i][j],f[i-1][j]+b[i]+c[i]);
if(j>1||i<e) cmin(f[i][j],f[i-1][j]+a[i]+d[i]);
cmin(f[i][j],f[i-1][j+1]+2*x[i]+a[i]+c[i]);
}
}
}
printf("%lld ",f[n][1]);
return 0;
}
E. 「JOI Open 2016」摩天大楼
有
为了规避掉绝对值,先排个序。
这题没有规定左右端点,考虑
把
考虑转移
#include"bits/stdc++.h"
using namespace std;
constexpr int N=115,L=1015,P=1e9+7;
#define inl inline
#define regi register
#define PII pair<int,int>
#define mkp(x,y) make_pair(x,y)
#define ll long long
//char buf_[1<<20],*_now=buf_,*_end=buf_;
//#define getchar() (_now==_end&&(_end=(_now=buf_)+fread(buf_,1,1<<20,stdin),_now==_end)?EOF:*_now++)
//namespace IO{void Unbind(){std::ios::sync_with_stdio(false);std::cin.tie(0);}}
inl void add_(ll &a,ll b,int p=P){return a=(a+b>=p?a+b-p:a+b),void();}
//inl int sub_(int a,int b,int p=P){return a-b<0?a-b+p:a-b;}
//inl int mul_(int a,int b,int p=P){return 1ll*a*b%p;}
inl int read(void)
{
int x=0;short f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) f=ch=='-'?-1:f;
for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
return x*f;
}
ll f[N][N][L][3];int a[N],n,l;
int main(void)
{
n=read(),l=read();
for(int i=1;i<=n;i++) a[i]=read();
sort(a+1,a+1+n);
f[0][0][0][0]=1;
for(int i=0;i<n;i++)
{
for(int j=0;j<=i;j++)
{
for(int k=0;k<=l;k++)
{
for(short d=0;d<3;d++)
{
ll p=k+(a[i+1]-a[i])*(2*j-d),t=f[i][j][k][d];
if(!t||p>l) continue;add_(f[i+1][j+1][p][d],t*(j+1-d)%P);
if(j>=2) add_(f[i+1][j-1][p][d],t*(j-1)%P);
if(j>=1) add_(f[i+1][j][p][d],t*(2*j-d)%P);
if(d<2)
{
add_(f[i+1][j+1][p][d+1],t*(2-d));
if(j>=1) add_(f[i+1][j][p][d+1],t*(2-d)%P);
}
}
}
}
}
ll ans=0;
for(int i=0;i<=l;i++) add_(ans,f[n][1][i][2]);
printf("%lld",n==1?1:ans);
return 0;
}
P2612 [ZJOI2012] 波浪
随机生成一个排列
排列的价值定义为相邻两个数差的绝对值累加
问价值大于等于
与上题类似,现在有
设计
转移类似上一题,注意得开
#include"bits/stdc++.h"
using namespace std;
constexpr int N=5015;
#define inl inline
#define regi register
#define PII pair<int,int>
#define mkp(x,y) make_pair(x,y)
#define ld __float128
//#define ll long long
//char buf_[1<<20],*_now=buf_,*_end=buf_;
//#define getchar() (_now==_end&&(_end=(_now=buf_)+fread(buf_,1,1<<20,stdin),_now==_end)?EOF:*_now++)
//namespace IO{void Unbind(){std::ios::sync_with_stdio(false);std::cin.tie(0);}}
//inl int add_(int a,int b,int p=P){return a+b>=p?a+b-p:a+b;}
//inl int sub_(int a,int b,int p=P){return a-b<0?a-b+p:a-b;}
//inl int mul_(int a,int b,int p=P){return 1ll*a*b%p;}
inl int read(void)
{
int x=0;short f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) f=ch=='-'?-1:f;
for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
return x*f;
}
int n,m,K;
template <class T>
void Print(T ans)
{
int num[100];
num[0]=0;
ans=ans*10;
for(int i=1;i<K;i++)
{
num[i]=(int)ans;
ans=(ans-num[i])*10;
}
num[K]=(int)(ans+0.5);
for(int i=K;i>=1;i--) if(num[i]>=10) num[i]-=10,num[i-1]++;
printf("%d.",num[0]);
for(int i=1;i<=K;i++) printf("%d",num[i]);
puts("");
}
int l;
ld ans;
template <class T>
void solve(T f[][115][3][N])
{
f[0][0][0][0]=1;
for(int i=0;i<n;i++)
{
int cur=i%2;
for(int j=0;j<=i+1;j++) for(int op=0;op<=2;op++) for(int s=0;s<=(n*n)/2;s++) f[!cur][j][op][s]=0;
for(int j=(i!=0);j<=i;j++) for(int op=0;op<=2;op++) for(int s=0,ts=2*j-op;ts<=(n*n)/2;s++,ts++)
{
if(!f[cur][j][op][s]) continue;
ld t=f[cur][j][op][s];
f[!cur][j+1][op][ts]=f[!cur][j+1][op][ts]+t*(j+1-op);
f[!cur][j][op][ts]+=t*(2*j-op);
if(j) f[!cur][j-1][op][ts]+=t*(j-1);
if(op!=2)
f[!cur][j+1][op+1][ts]+=t*(2-op),f[!cur][j][op+1][ts]+=t*(2-op);
}
}
for(int i=l;i<=5000;i++) ans=(ans+f[n%2][1][2][i]);
for(int i=1;i<=n;i++) ans=ans/i;
Print(ans);
}
ld f1[2][115][3][N];
double f2[2][115][3][N];
int main(void)
{
n=read(),l=read(),K=read();
if(n==1) Print(1),exit(0);
if(n<=8) solve(f2);else solve(f1);
return 0;
}
CF1515E Phoenix and Computers
给定
现在你可以随意打开电脑,但如果第
问你有多少种打开电脑的方法,使得最后所有电脑都是开着的
转移有 新建段,段扩增,段合并 共五种情况
#include"bits/stdc++.h"
using namespace std;
constexpr int N=415;
#define inl inline
#define regi register
#define PII pair<int,int>
#define mkp(x,y) make_pair(x,y)
#define ll long long
//char buf_[1<<20],*_now=buf_,*_end=buf_;
//#define getchar() (_now==_end&&(_end=(_now=buf_)+fread(buf_,1,1<<20,stdin),_now==_end)?EOF:*_now++)
//namespace IO{void Unbind(){std::ios::sync_with_stdio(false);std::cin.tie(0);}}
//inl void add_(ll &a,ll b,int p=P){return a=(a+b>=p?a+b-p:a+b),void();}
//inl int sub_(int a,int b,int p=P){return a-b<0?a-b+p:a-b;}
//inl int mul_(int a,int b,int p=P){return 1ll*a*b%p;}
inl int read(void)
{
int x=0;short f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) f=ch=='-'?-1:f;
for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
return x*f;
}
int n,P;
ll f[N][N];
int main(void)
{
n=read(),P=read(),f[0][0]=1;
for(int i=1;i<=n;i++) for(int j=1;j<=i;j++)
{
f[i][j]+=f[i-1][j]*2*j;
if(i>=2) f[i][j]+=f[i-2][j]*2*j,f[i][j]+=f[i-2][j+1]*2*j;
if(i>=3) f[i][j]+=f[i-3][j+1]*j;
(f[i][j]+=f[i-1][j-1]*j)%=P;
}
printf("%lld",f[n][1]);
return 0;
}
P7967 [COCI2021-2022#2] Magneti
给定
求所有磁铁互不吸引的方案总数对
最后还需要组合数一下。对于
转化为
#include"bits/stdc++.h"
using namespace std;
constexpr int N=2e5+15,P=1e9+7;
#define inl inline
#define regi register int
#define PII pair<int,int>
#define mkp(x,y) make_pair(x,y)
#define ll long long
//char buf_[1<<20],*_now=buf_,*_end=buf_;
//#define getchar() (_now==_end&&(_end=(_now=buf_)+fread(buf_,1,1<<20,stdin),_now==_end)?EOF:*_now++)
//namespace IO{void Unbind(){std::ios::sync_with_stdio(false);std::cin.tie(0);}}
//inl int add_(int a,int b,int p=P){return a+b>=p?a+b-p:a+b;}
//inl int sub_(int a,int b,int p=P){return a-b<0?a-b+p:a-b;}
//inl int mul_(int a,int b,int p=P){return 1ll*a*b%p;}
inl int read(void)
{
int x=0;short f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) f=ch=='-'?-1:f;
for(;ch>='0'&&ch<='9';x=(x<<3)+(x<<1)+(ch^48),ch=getchar());
return x*f;
}
ll fac[N],inv[N];
ll qpow(ll a,ll b){ll res=1;a%=P;while(b){if(b&1)res=res*a%P;a=a*a%P;b>>=1;}return res;}
void init(int n_)
{
fac[0]=inv[0]=1;
for(regi i=1;i<=n_;i++) fac[i]=fac[i-1]*i%P;
inv[n_]=qpow(fac[n_],P-2);
for(regi i=n_-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%P;
}
ll C(ll n,ll m){return fac[n]*inv[n-m]%P*inv[m]%P;}
ll n,l,r[N],ans;
ll f[65][65][10015];
int main(void)
{
// freopen("magnet.in","r",stdin),freopen("magnet.out","w",stdout);
n=read(),l=read();
init(20000);
for(int i=1;i<=n;i++) r[i]=read();
sort(r+1,r+1+n);
f[0][0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=i;j++)
{
for(int s=0;s<=l;s++)
{
if(s>=r[i]) (f[i][j][s]+=f[i-1][j][s-r[i]]*j*2)%=P;
if(s>=1) (f[i][j][s]+=f[i-1][j-1][s-1]*j)%=P;
if(s>=2*r[i]-1) (f[i][j][s]+=f[i-1][j+1][s-2*r[i]+1]*j)%=P;
}
}
}
for(int i=0;i<=l;i++) ans=(ans+(f[n][1][i]*C(l+n-i,n))%P)%P;
printf("%lld",ans);
return 0;
}
再附上我们强大的
本文来自博客园,作者:Ech0_7,转载请注明原文链接:https://www.cnblogs.com/empty-space/p/18535786
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具