再见了,所有的 Educational DP
A - Frog 1
线性 DP。
状态转移方程为
,注意边界。
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+5;
int n,a[N];
int f[N];
inline int ABS(int x){return max(x,-x);}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
f[2]=ABS(a[2]-a[1]);
for(int i=3;i<=n;i++)
f[i]=min(f[i-1]+ABS(a[i]-a[i-1]),f[i-2]+ABS(a[i]-a[i-2]));
cout<<f[n]<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/07
//When I wrote a piece of code, her and I knew what it was doing
B - Frog 2
线性 DP。
状态转移方程为
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e5+5;
int n,m,a[N];
inline int ABS(int x){return max(x,-x);}
int f[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
memset(f,0x3f,sizeof(f));
f[1]=0;
for(int i=2;i<=n;i++)
for(int j=max(1,i-m);j<i;j++)
f[i]=min(f[i],f[j]+ABS(a[i]-a[j]));
cout<<f[n]<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/07
//When I wrote a piece of code, her and I knew what it was doing
C - Vacation
线性 DP。
设
代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+5;
int n,a[N],b[N],c[N];
int f[N][3];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i]>>b[i]>>c[i];
for(int i=1;i<=n;i++){
f[i][0]=max(f[i-1][1],f[i-1][2])+a[i];
f[i][1]=max(f[i-1][0],f[i-1][2])+b[i];
f[i][2]=max(f[i-1][0],f[i-1][1])+c[i];
}
cout<<max(max(f[n][0],f[n][1]),f[n][2])<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/07
//When I wrote a piece of code, her and I knew what it was doing
D - Knapsack 1
背包 DP。
设
使用滚动数组或者一种广为人知的动态更新可以做到
代码
#include<iostream>
#include<cstdio>
using namespace std;
using ll=long long;
const int N=1e5+5;
int n,m;
ll f[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1,v,w;i<=n;i++){
cin>>w>>v;
for(int j=m;j>=w;j--)f[j]=max(f[j],f[j-w]+v);
}
cout<<f[m]<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/07
//When I wrote a piece of code, her and I knew what it was doing
E - Knapsack 2
背包 DP。
设
同样可以用滚动数组或者一种广为人知的动态更新可以做到
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
using ll=long long;
const int N=1e5+5;
int n,m;
ll f[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1,w,v;i<=n;i++){
cin>>w>>v;
for(int j=100000;j>=v;j--)f[j]=min(f[j],f[j-v]+w);
}
for(int i=100000;~i;i--)
if(f[i]<=m)return cout<<i<<endl,0;
return 0;
}
//coder:Iictiw
//date:24/11/07
//When I wrote a piece of code, her and I knew what it was doing
F - LCS
经典题。
设
记录转移以输出方案。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
using pii=pair<int,int>;
const int N=3005;
int n,m;
string s,t;
int f[N][N];
pii pa[N][N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>s>>t;n=s.size(),m=t.size();s=' '+s,t=' '+t;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(f[i][j-1]>f[i-1][j])f[i][j]=f[i][j-1],pa[i][j]={i,j-1};
else f[i][j]=f[i-1][j],pa[i][j]={i-1,j};
if(f[i-1][j-1]+(s[i]==t[j])>f[i][j])f[i][j]=f[i-1][j-1]+(s[i]==t[j]),pa[i][j]={i-1,j-1};
}
string ans;
pii p={n,m};
while(p.first&&p.second){
pii ne=pa[p.first][p.second];
if(ne.first==p.first-1&&ne.second==p.second-1&&s[p.first]==t[p.second])ans+=s[p.first];
p=ne;
}
reverse(ans.begin(),ans.end());
cout<<ans<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/07
//When I wrote a piece of code, her and I knew what it was doing
G - Longest Path
DAG 上 DP。
状态转移方程为
代码
#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;
const int N=1e5+5;
int n,m;
vector<int>gr[N];
int ind[N];
int f[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1,u,v;i<=m;i++){
cin>>u>>v;
gr[u].push_back(v),ind[v]++;
}
queue<int>q;
for(int i=1;i<=n;i++)if(!ind[i])q.push(i);
while(q.size()){
int p=q.front();
q.pop();
for(auto to:gr[p]){
f[to]=max(f[to],f[p]+1);
if(!--ind[to])q.push(to);
}
}
int ans=0;
for(int i=1;i<=n;i++)ans=max(ans,f[i]);
cout<<ans<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/07
//When I wrote a piece of code, her and I knew what it was doing
H - Grid 1
状态转移方程为
代码
#include<iostream>
#include<cstdio>
#define mod 1000000007
using namespace std;
using ll=long long;
const int N=1005;
int n,m;
char ch[N][N];
ll f[N][N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>ch[i][j];
f[1][1]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(ch[i][j]=='.')(f[i][j]+=f[i-1][j]+f[i][j-1])%=mod;
cout<<f[n][m]<<endl;
return 0;
}
I - Coins
期望 DP。
设
代码
#include<iostream>
#include<cstdio>
using namespace std;
using db=double;
const int N=3005;
int n;db a[N];
db f[2][N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
int cur=0;
f[0][0]=1;
for(int i=1;i<=n;i++){
cur^=1;
for(int j=0;j<=i;j++)f[cur][j]=0;
f[cur][0]=f[cur^1][0]*(1-a[i]);
for(int j=1;j<=i;j++)f[cur][j]+=f[cur^1][j]*(1-a[i])+f[cur^1][j-1]*a[i];
}
db ans=0;
for(int i=n/2+1;i<=n;i++)ans+=f[cur][i];
printf("%.9lf\n",ans);
return 0;
}
//coder:Iictiw
//date:24/11/07
//When I wrote a piece of code, her and I knew what it was doing
J - Sushi
期望 DP。
设
整理得
代码
#include<iostream>
#include<cstdio>
using namespace std;
using db=double;
const int N=305;
int n,a,b,c;
db f[N][N][N];
db dfs(int a,int b,int c){
if(!a&&!b&&!c)return 0;
if(a<0||b<0||c<0)return 0;
if(f[a][b][c]!=-1)return f[a][b][c];
db&ret=f[a][b][c];ret=0;
ret=((db)a*dfs(a-1,b,c)+(db)b*dfs(a+1,b-1,c)+(db)c*dfs(a,b+1,c-1)+n)/(db)(a+b+c);
return ret;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1,x;i<=n;i++){
cin>>x;
if(x==1)a++;
else if(x==2)b++;
else if(x==3)c++;
}
for(int i=0;i<=n;i++)
for(int j=0;j<=n;j++)
for(int k=0;k<=n;k++)
f[i][j][k]=-1;
printf("%.9lf\n",dfs(a,b,c));
return 0;
}
//coder:Iictiw
//date:24/11/07
//When I wrote a piece of code, her and I knew what it was doing
K - Stones
博弈论。
设
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1e5+5;
int n,k,a[N];
int f[N];
bool dfs(int k){
if(f[k]!=-1)return f[k];
f[k]=0;
for(int i=1;i<=n;i++)
if(k>=a[i])f[k]|=!dfs(k-a[i]);
return f[k];
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>k;
for(int i=1;i<=n;i++)cin>>a[i];
memset(f,-1,sizeof(f));
if(dfs(k))cout<<"First"<<endl;
else cout<<"Second"<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/14
//When I wrote a piece of code, her and I knew what it was doing
L - Deque
博弈论。
设
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
using ll=long long;
const int N=3005;
int n,a[N];
ll f[N][N];
inline ll dfs(int l,int r){
if(l==r)return a[l];
if(l>r)return 0;
if(f[l][r]!=-1)return f[l][r];
return f[l][r]=max(a[l]-dfs(l+1,r),a[r]-dfs(l,r-1));
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
memset(f,-1,sizeof(f));
cout<<dfs(1,n)<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/08
//When I wrote a piece of code, her and I knew what it was doing
M - Candies
前缀和优化 DP。
设
易用前缀和优化至
代码
#include<iostream>
#include<cstdio>
#define mod 1000000007
using namespace std;
using ll=long long;
const int N=105,M=1e5+5;
int n,m,a[N];
int f[2][M],g[M];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
f[0][0]=1;
int cur=0;
for(int i=1;i<=n;i++){
g[0]=f[cur][0];
for(int j=1;j<=m;j++)g[j]=(g[j-1]+f[cur][j])%mod;
cur^=1;
for(int j=0;j<=m;j++)f[cur][j]=0;
for(int j=0;j<=m;j++)f[cur][j]=((g[j]-(j-a[i]<=0?0:g[j-a[i]-1]))%mod+mod)%mod;
}
cout<<f[cur][m]<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/08
//When I wrote a piece of code, her and I knew what it was doing
N - Slimes
区间 DP。
设
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
using ll=long long;
const int N=405;
const ll inf=1e16+5;
int n,a[N];ll s[N];
ll f[N][N];
ll dfs(int l,int r){
if(l>=r)return 0;
if(f[l][r]!=-1)return f[l][r];
ll&ret=f[l][r];ret=inf;
for(int i=l;i<r;i++)
ret=min(ret,dfs(l,i)+dfs(i+1,r)+s[r]-s[l-1]);
return ret;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i],s[i]=s[i-1]+a[i];
memset(f,-1,sizeof(f));
cout<<dfs(1,n)<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/08
//When I wrote a piece of code, her and I knew what it was doing
O - Matching
状压 DP。
设
代码
#include<iostream>
#include<cstdio>
#define mod 1000000007
using namespace std;
using ll=long long;
const int N=21;
int n,a[N][N];
ll f[1<<N][N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>a[i][j];
for(int i=0;i<n;i++)if(a[0][i])f[1<<i][0]=1;
for(int i=0;i<n-1;i++)
for(int S=0;S<(1<<n);S++){
if(f[S][i]==0)continue;
for(int j=0;j<n;j++){
if((S>>j)&1||!a[i+1][j])continue;
(f[S|(1<<j)][i+1]+=f[S][i])%=mod;
}
}
cout<<f[(1<<n)-1][n-1]<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/08
//When I wrote a piece of code, her and I knew what it was doing
P - Independent Set
树上 DP。
设
代码
#include<iostream>
#include<cstdio>
#include<vector>
#define mod 1000000007
using namespace std;
using ll=long long;
const int N=1e5+5;
int n;
vector<int>gr[N];
ll f[N][2];
void dfs(int p,int fa){
f[p][0]=f[p][1]=1;
for(auto to:gr[p]){
if(to==fa)continue;
dfs(to,p);
(f[p][0]*=f[to][0]+f[to][1])%=mod;
(f[p][1]*=f[to][0])%=mod;
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
gr[u].push_back(v),gr[v].push_back(u);
}
dfs(1,0);
cout<<(f[1][0]+f[1][1])%mod<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/09
//When I wrote a piece of code, her and I knew what it was doing
Q - Flowers
数据结构优化 DP。
设
注意到这相当于一个二维偏序,因此可以用 BIT 优化至线性对数时间复杂度。
代码
#include<iostream>
#include<cstdio>
using namespace std;
using ll=long long;
const int N=2e5+5;
int n,a[N],h[N];
ll f[N];
ll t[N];
inline void update(int i,ll x){for(i++;i<=n+1;i+=i&-i)t[i]=max(t[i],x);}
inline ll query(int i){ll ret=0;for(i++;i;i-=i&-i)ret=max(ret,t[i]);return ret;}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>h[i];
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=n;i++){
f[i]=query(h[i]-1)+a[i];
update(h[i],f[i]);
}
cout<<query(n)<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/09
//When I wrote a piece of code, her and I knew what it was doing
R - Walk
矩阵乘法加速 DP。
设
可以将转移写成矩阵乘法的形式,做到
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define mod 1000000007
using namespace std;
using ll=long long;
const int N=55;
int n;ll m;
struct matrix{
ll a[N][N];
int n,m;
matrix(){
n=m=0;
memset(a,0,sizeof(a));
}
friend matrix operator*(matrix a,matrix b){
matrix c;c.n=a.n,c.m=b.m;
for(int k=1;k<=a.m;k++)
for(int i=1;i<=a.n;i++)
for(int j=1;j<=b.m;j++)
(c.a[i][j]+=a.a[i][k]*b.a[k][j])%=mod;
return c;
}
};
inline matrix qpow(matrix a,ll p){
matrix ret;ret.n=a.n,ret.m=a.m;
for(int i=1;i<=ret.n;i++)ret.a[i][i]=1;
while(p){
if(p&1)ret=ret*a;
a=a*a,p>>=1;
}
return ret;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
matrix I,C;C.n=n,C.m=n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>C.a[i][j];
I.n=1,I.m=n;
for(int i=1;i<=n;i++)I.a[1][i]=1;
C=qpow(C,m);
I=I*C;
ll ans=0;
for(int i=1;i<=n;i++)(ans+=I.a[1][i])%=mod;
cout<<ans<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/09
//When I wrote a piece of code, her and I knew what it was doing
S - Digit Sum
数位 DP。
设
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#define mod 1000000007
using namespace std;
using ll=long long;
const int N=1e4+5;
int n;string s;
ll f[N][105];
int num[N];
ll dfs(int p,int s,bool limit){
if(!p)return s==0;
if(!limit&&f[p][s]!=-1)return f[p][s];
ll ret=0;int up=limit?num[p]:9;
for(int i=0;i<=up;i++)(ret+=dfs(p-1,(s+i)%n,limit&&i==up))%=mod;
if(!limit)f[p][s]=ret;
return ret;
}
ll solve(string s){
int tot=0;
for(int i=(int)s.size()-1;i;i--)num[++tot]=s[i]-'0';
memset(f,-1,sizeof(f));
return dfs(tot,0,1);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>s>>n;s=' '+s;
cout<<(solve(s)-1+mod)%mod<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/09
//When I wrote a piece of code, her and I knew what it was doing
T - Permutation
前缀和优化 DP。
开始上强度了。
设
由此,有转移
不难使用前缀和优化转移。
代码
#include<iostream>
#include<cstdio>
#define mod 1000000007
using namespace std;
using ll=long long;
const int N=3005;
int n;string s;
ll f[N][N],g[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>s;s=' '+s;
f[1][1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++)g[j]=(g[j-1]+f[i-1][j])%mod;
for(int j=1;j<=i;j++)
if(s[i-1]=='>')f[i][j]=(g[i-1]-g[j-1]+mod)%mod;
else f[i][j]=g[j-1];
}
ll ans=0;
for(int j=1;j<=n;j++)(ans+=f[n][j])%=mod;
cout<<ans<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/10
//When I wrote a piece of code, her and I knew what it was doing
U - Grouping
状压 DP。
设
其中,
代码
#include<iostream>
#include<cstdio>
using namespace std;
using ll=long long;
const int N=17;
int n,a[N][N];
ll f[1<<N],g[1<<N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
cin>>a[i][j];
for(int S=0;S<(1<<n);S++)
for(int i=0;i<n;i++){
if(!((S>>i)&1))continue;
for(int j=i+1;j<n;j++){
if(!((S>>j)&1))continue;
g[S]+=a[i][j];
}
}
for(int S=0;S<(1<<n);S++)
for(int T=S;T;T=S&(T-1))
f[S]=max(f[S],f[S-T]+g[T]);
cout<<f[(1<<n)-1]<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/11
//When I wrote a piece of code, her and I knew what it was doing
V - Subtree
换根 DP。
设点
考虑换根,设
最终
考虑如何快速计算
考虑维护每个点的所有儿子的
代码
#include<iostream>
#include<cstdio>
#include<vector>
#define int long long
using namespace std;
using ll=long long;
const int N=1e5+5;
int n,mod;
vector<int>gr[N];
ll f[N],g[N],h[N];
void dfs(int p,int fa){
f[p]=1;
for(auto to:gr[p]){
if(to==fa)continue;
dfs(to,p),(f[p]*=f[to]+1)%=mod;
}
ll res=1;
for(int i=0;i<(int)gr[p].size();i++){
int to=gr[p][i];if(to==fa)continue;
g[to]=res,(res*=f[to]+1)%=mod;
}
res=1;
for(int i=gr[p].size()-1;~i;i--){
int to=gr[p][i];if(to==fa)continue;
(g[to]*=res)%=mod,(res*=f[to]+1)%=mod;
}
}
void redfs(int p,int fa){
if(p!=1)h[p]=(h[fa]*g[p]+1)%mod;
for(auto to:gr[p])if(to!=fa)redfs(to,p);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>mod;
for(int i=1,u,v;i<n;i++){
cin>>u>>v;
gr[u].push_back(v);gr[v].push_back(u);
}
dfs(1,0);
h[1]=1,redfs(1,0);
for(int i=1;i<=n;i++)cout<<h[i]*f[i]%mod<<'\n';
return 0;
}
//coder:Iictiw
//date:24/11/11
//When I wrote a piece of code, her and I knew what it was doing
W - Intervals
数据结构优化 DP。
设
对于这类区间带权的题,一种套路化的处理方法是在右端点处更新答案。考虑转移,若在第
若在第
直接做是
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
using ll=long long;
const int N=2e5+5;
int n,m;
struct node{int l,r,x;}a[N];
#define mid ((l+r)>>1)
#define ls (p<<1)
#define rs (p<<1|1)
ll mx[N<<2],tag[N<<2];
inline void push_up(int p){mx[p]=max(mx[ls],mx[rs]);}
inline void make_tag(int p,ll x){mx[p]+=x,tag[p]+=x;}
inline void push_down(int p){
if(tag[p]){
make_tag(ls,tag[p]),make_tag(rs,tag[p]);
tag[p]=0;
}
}
void update(int p,int l,int r,int L,int R,ll x){
if(l>=L&&r<=R)return make_tag(p,x);
if(l>R||r<L)return;
push_down(p);
update(ls,l,mid,L,R,x),update(rs,mid+1,r,L,R,x);
push_up(p);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++)cin>>a[i].l>>a[i].r>>a[i].x;
sort(a+1,a+1+m,[&](node x,node y){
return x.r<y.r;
});
for(int i=1,j=1;i<=n;i++){
update(1,1,n,i,i,max(mx[1],0ll));
while(j<=m&&a[j].r<=i)update(1,1,n,a[j].l,a[j].r,a[j].x),j++;
}
cout<<max(0ll,mx[1])<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/14
//When I wrote a piece of code, her and I knew what it was doing
X - Tower
贪心维护 DP 转移顺序。
Lemma: 必然存在一种最优策略,使得若第
证明:考虑邻项交换,对于相邻的两个块
因此,可以将所有块以
设
若放第
若第
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
using ll=long long;
const int N=1e4+5;
int n,m;
struct node{int w,s,v;}a[N];
ll f[2][N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i].w>>a[i].s>>a[i].v,m=max(m,a[i].s);
sort(a+1,a+1+n,[&](node x,node y){
return x.w+x.s>y.w+y.s;
});
int cur=0;
f[0][a[1].s]=a[1].v;
for(int i=2;i<=n;i++){
cur^=1;
for(int j=0;j<=m;j++)f[cur][j]=f[cur^1][j];
f[cur][a[i].s]=max(f[cur][a[i].s],(ll)a[i].v);
for(int j=0;j<=m;j++)
if(a[i].w<=j)
f[cur][min(a[i].s,j-a[i].w)]=max(f[cur][min(a[i].s,j-a[i].w)],f[cur^1][j]+a[i].v);
}
ll ans=0;
for(int i=0;i<=m;i++)ans=max(ans,f[cur][i]);
cout<<ans<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/14
//When I wrote a piece of code, her and I knew what it was doing
Y - Grid 2
容斥 DP。
直接计算方案数较为困难,考虑容斥,用总方案数减去经过墙的方案数。
一个直接的想法是经过
考虑将经过墙的方案数不重不漏地表示出来。设
特别地,我们不妨设第
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define x first
#define y second
#define mod 1000000007
using namespace std;
using ll=long long;
const int N=2e5+5;
int n,m,k;
pair<int,int>a[N];
ll fct[N],ifct[N];
inline ll qpow(ll a,int p){
ll ret=1;
while(p){
if(p&1)(ret*=a)%=mod;
(a*=a)%=mod,p>>=1;
}
return ret;
}
inline ll C(int n,int m){
if(n<0||m<0||n<m)return 0;
return fct[n]*ifct[m]%mod*ifct[n-m]%mod;
}
ll f[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
for(int i=fct[0]=1;i<N;i++)fct[i]=fct[i-1]*i%mod;
ifct[N-1]=qpow(fct[N-1],mod-2);
for(int i=N-2;~i;i--)ifct[i]=ifct[i+1]*(i+1)%mod;
cin>>n>>m>>k;
for(int i=1;i<=k;i++)cin>>a[i].x>>a[i].y;
a[++k]={n,m};
sort(a+1,a+1+k);
for(int i=1;i<=k;i++){
f[i]=C(a[i].x+a[i].y-2,a[i].x-1);
for(int j=1;j<i;j++)
if(a[j].y<=a[i].y)
f[i]=(f[i]-f[j]*C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)%mod+mod)%mod;
}
cout<<f[k]<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/15
//When I wrote a piece of code, her and I knew what it was doing
Z - Frog 3
斜率优化 DP。
请自行脑补 BGM
设
不妨设此时有两个决策点
化简得
注意到
即
由于题目保证 h 单调递增,因此
如果我们将
类似与上式可得,若点
若点
我们不妨再设有
意味着什么。
分类讨论,设
-
当
时: 优于 ; -
当
时: 不劣于 , 不劣于 ; -
当
时: 优于 。
不难发现,当
在坐标轴上,这表现为直线
由此可得,所有可能的最优决策点满足斜率单调递增。
删去所有不可能成为最优决策点的点,不难发现,剩下的点将会构成一个下凸壳:
因此,这种优化方法又称为凸壳优化。
回到本题,考虑如何维护该下凸壳。不难发现本题中,决策点的横坐标(即
查询时,即寻找斜率大于
但在本题中,
结束了?并没有。
从头开始,回到原转移方程
设
不妨假设有一条斜率为
可以看出,所有可能的最优决策点必然位于下凸壳上,每次要找的点也就是凸壳上第一个斜率大于
维护下凸壳和寻找最优决策点,这与上一种做法殊途同归。
代码(二分栈)
#include<iostream>
#include<cstdio>
using namespace std;
using db=double;
using ll=long long;
const int N=2e5+5;
int n;ll C,a[N];
ll f[N];
int st[N],top;
inline db g(int i){return f[i]+a[i]*a[i];}
inline db slope(int i,int j){return (g(i)-g(j))/(a[i]-a[j]);}
inline int calc(db k){
int l=1,r=top-1,ret=top;
while(l<=r){
int mid=(l+r)>>1;
if(slope(st[mid],st[mid+1])>=k)r=mid-1,ret=mid;
else l=mid+1;
}
return st[ret];
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>C;
for(int i=1;i<=n;i++)cin>>a[i];
st[++top]=1;
for(int i=2;i<=n;i++){
int j=calc(2*a[i]);
f[i]=f[j]+(a[i]-a[j])*(a[i]-a[j])+C;
while(top>1&&slope(st[top-1],st[top])>=slope(st[top],i))top--;
st[++top]=i;
}
cout<<f[n]<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/18
//When I wrote a piece of code, her and I knew what it was doing
代码(单调队列)
#include<iostream>
#include<cstdio>
using namespace std;
using db=double;
using ll=long long;
const int N=2e5+5;
int n;ll C,a[N];
ll f[N];
int L=0,R=-1,Q[N];
inline db g(int i){return f[i]+a[i]*a[i];}
inline db slope(int i,int j){return (g(i)-g(j))/(a[i]-a[j]);}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>C;
for(int i=1;i<=n;i++)cin>>a[i];
Q[++R]=1;
for(int i=2;i<=n;i++){
while(L<R&&slope(Q[L],Q[L+1])<=2*a[i])L++;
f[i]=f[Q[L]]+(a[i]-a[Q[L]])*(a[i]-a[Q[L]])+C;
while(L<R&&slope(Q[R-1],Q[R])>=slope(Q[R],i))R--;
Q[++R]=i;
}
cout<<f[n]<<endl;
return 0;
}
//coder:Iictiw
//date:24/11/18
//When I wrote a piece of code, her and I knew what it was doing
后记
结束了?结束了。
结束了?没结束。
结束了?结束了。
Fumo?fumo。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探