dp 专练
A. CF1810G
发现脑子不太够用,考虑先解决小情况。
设 \(dp_{i,j}\) 表示从后往前枚举到第 \(i\) 个位置,且后缀中前缀和最大为 \(j\),那么期望是多少,转移为:
\[p_i\times dp_{i,j}\to dp_{i-1,j+1}
\]
\[(1-p_i)\times dp_{i,j}\to dp_{i-1,\max(j-1,0)}
\]
由于是倒着枚举的,所以只能先枚举 \(k\),再进行 \(O(n^2)\) 的 \(dp\),过于浪费。考虑反转 \(dp\)。
有趣的是,\(dp\) 不仅可以记录历史信息,还可以用以规划未来。我们将 \(f_{i,j}\) 设为从前往后枚举,\(i\) 以后的位置钦定前缀和最大值为 \(j\) 的情况,转移方程为:
\[f_{i,j}=p_i\times f_{i-1,j+1}+(1-p_i)\times f_{i-1,\max(j-1,0)}
\]
为了方便计算答案,我们设 \(dp_{0,i}=h_i\),则有 \(ans_k=dp_{k,0}\)。
时间复杂度 \(O(n^2)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=5005,p=1e9+7;
int t,n,pc[N],f[N][N];
inline int qpow(int x,int y){
int re=1;
while(y){
if(y&1) re=1ll*re*x%p;
x=1ll*x*x%p,y>>=1;
}
return re;
}
inline void solve(){
cin>>n;
for(int i=1,x,y;i<=n;i++)
cin>>x>>y,pc[i]=1ll*x*qpow(y,p-2)%p;
for(int i=0;i<=n+1;i++)
for(int j=0;j<=n+1;j++) f[i][j]=0;
for(int i=0;i<=n;i++) cin>>f[0][i];
for(int i=1;i<=n;i++){
for(int j=0;j<=n;j++)
f[i][j]=(1ll*pc[i]*f[i-1][j+1]+(p-pc[i]+1ll)*f[i-1][max(j-1,0)])%p;
cout<<f[i][0]<<" ";
}
cout<<"\n";
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>t;
while(t--) solve();
return 0;
}
B. AGC061C
考虑不合法情况就是对于一个 \(i\),我们选择了 \(b_i\),且 \((a_i,b_i)\) 中没有选一个。我们考虑容斥解决。
设 \(f_i\) 表示枚举了前 \(i\) 个数该怎么放。假如不考虑限制,转移显然为 \(f_i=2f_{i-1}\)。考虑要减去什么。
对于一个 \(i\),假如它不合法,那么就会导致所有与它有交的区间选择是确定的。所以找到 \(p=\operatorname{arcmax}_{j=1}^n [b_j<a_i]\times j\) 和 \(q=\operatorname{arcmax}_{j=1}^n[a_j<b_i]\times j\),则 \(dp_q\) 会减去 \(dp_p\),双指针即可。
时间复杂度 \(O(n)\)。
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+5,p=998244353;
int n,a[N],b[N],f[N];
vector<int>g[N];
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i];
f[0]=1;
for(int i=1,l=0,r=0;i<=n;i++){
while(l<n&&b[l+1]<a[i]) l++;
while(r<n&&a[r+1]<b[i]) r++;
g[r].push_back(l);
f[i]=f[i-1]*2%p;
for(int j:g[i])
f[i]=(f[i]-f[j]+p)%p;
}
cout<<f[n];
return 0;
}
E. ABC290Ex
猫狗大舞台,有命你就来。
赌博题。先赌猫狗分别先升后降,再赌猫狗左右各一半,最后再赌猫大的一半放左,狗大的一半放右一定最优。剩下的就是简单模拟了。
时间复杂度 \(O(n\log n)\)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=305;
int n,m,a[N],b[N],numl,numr,ans;
struct num{
int x,op;
}anm[N<<1];
inline int cmp(num x,num y){
return x.x<y.x;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=m;i++) cin>>b[i];
sort(a+1,a+n+1),sort(b+1,b+m+1);
if(n&1){
for(int i=1;i<=m;i++) ans+=b[i];
ans+=(m&1)*a[n--];
}
if(m&1){
for(int i=1;i<=n;i++) ans+=a[i];
m--;
}
for(int i=1;i<=n;i++) anm[i]={a[i],0};
for(int i=1;i<=m;i++) anm[i+n]={b[i],1};
sort(anm+1,anm+n+m+1,cmp);
for(int i=n+m,j=0,k=0;i;i--){
if(!anm[i].op){
if(numl<n/2)
ans+=(2*max(m/2,k)-m)*anm[i].x,numl++;
else ans+=2*min(m/2,k)*anm[i].x;
j++;
}
else{
if(numr<m/2)
ans+=(2*max(n/2,j)-n)*anm[i].x,numr++;
else ans+=2*min(n/2,j)*anm[i].x;
k++;
}
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号