YL 模拟赛总结 9
Problem
T1
我们考虑一种贪心策略:对于价格前 \(n-1\) 小的咖啡,我们求出一种最优方案使得按照此方案买完咖啡后钱数 \(\ge 20\) 且最接近 \(20\)。
至于如何求出最优方案,进行一遍 01 背包即可。
#include<bits/stdc++.h> using namespace std; int n,k; int a[1031],dp[1031]; int main(){ //freopen("overdraft.in","r",stdin); //freopen("overdraft.out","w",stdout); ios::sync_with_stdio(0); cin>>n>>k; for(int i=1;i<=n;i++) cin>>a[i]; if(k<=20){ cout<<k; return 0; } sort(a+1,a+n+1); for(int i=1;i<n;i++) for(int j=k-20;j>=a[i];j--) dp[j]=max(dp[j],dp[j-a[i]]+a[i]); cout<<k-dp[k-20]-a[n]; return 0; }
T2
显然,题目要求的即为树的重心到每个店的距离之和。
考虑树形 dp。
令 \(s_x\) 为节点 \(x\) 的“向下”的子树大小,则有:
(其中 \(S\) 为节点 \(x\) 的子节点的集合)
再令 \(f_x\) 为节点 \(x\) “向上”的字数大小,则有:
(其中 \(V\) 为所有节点的全集)
根据 \(f_x\) 的计算式,我们知道 \(f_x\) 是包含了 \(s_x\) 的。
因此对于所有节点,取 \(f\) 值最大的即为树的重心。
剩下的求距离仅需再次 dfs 一遍即可解决。
#include<bits/stdc++.h> using namespace std; int n,ans,C; int g[100031],f[100031]; bool vis[100031]; vector<int> G[100031]; void dfs(int x){ vis[x]=1; for(auto i:G[x]){ if(!vis[i]){ dfs(i); g[x]+=g[i]+1,f[x]=max(f[x],g[i]+1); } } f[x]=max(f[x],n-g[x]-1); vis[x]=0; } int main(){ cin>>n; for(int i=1,a,b;i<n;i++){ cin>>a>>b; G[a].push_back(b); G[b].push_back(a); } dfs(1); f[0]=1e9+31; for(int i=1;i<=n;i++) if(f[C]>f[i]) C=i; //cout<<C<<' '; memset(g,0,sizeof(g)); dfs(C); for(int i=1;i<=n;i++) ans+=g[i]; cout<<ans; return 0; }
T3
考虑贪心地使用 \(k\) 张优惠券。
具体而言,维护三个优先队列,分别表示原价、优惠价、和差价。
然后模拟买咖啡的过程。若当前的最小优惠价 \(+\) 最小差价 \(<\) 最小原价,则使用一张优惠券。
否则,说明当前使用优惠券不比买原价更优,直接买原价即可。
#include<bits/stdc++.h> #define int long long using namespace std; int n,m,k,ans; int p[20031],c[20031]; struct node{ int val,id; friend bool operator < (node a,node b){ return a.val>b.val; } }; bool vis[20031]; priority_queue<node> a,b; priority_queue<int,vector<int>,greater<int> > d; signed main(){ cin>>n>>m>>k; for(int i=1;i<=n;i++) cin>>p[i]>>c[i],a.push({p[i],i}),b.push({c[i],i}); for(int i=1;i<=k;i++) d.push(0); while(m>0&&ans<n){ while(vis[a.top().id]) a.pop(); while(vis[b.top().id]) b.pop(); if(d.top()+b.top().val<a.top().val){ node t=b.top(); int x=t.val+d.top(); if(m<x) break; m-=x,d.pop(),d.push(p[t.id]-c[t.id]),vis[t.id]=1; } else{ node t=a.top(); int x=t.val; if(m<x) break; m-=x,vis[t.id]=1; } ans++; } cout<<ans; return 0; }
T4
考虑进行线性 dp 求解。
我们令 \(dp_{i,j}\) 表示选择 \(i\) 个物品,总重量为 \(j\) 时的方案总数。
转移方程即为
这样直接计算,时间复杂度为 \(O(n^2)\) 无法接受。
考虑进行前缀和优化。
维护一个前缀和数组 \(sum_{i,j}\),首先 \(dp_{i,j}\) 至少是 \(sum_{i-1,j \div 2}\)。
然后分类讨论即可:
-
若 \(j>n\),则为了保持决策区间长度为 \(n+1\)(包括当前的),则 \(dp_{i,j}\) 需要减去 \(sum_{i-1,j-n-1}\)。
-
若 \(j \div 2 \le y \le j\),则 \(j-y \sim j\) 这个决策区间应该被去除,即 \(dp_{i,j}\) 需要减去 \(dp_{i-1,j-y}\)。
#include<bits/stdc++.h> using namespace std; const int MOD=998244353; int n,x,y,ans; int dp[31][100031],sum[31][100031]; int main(){ cin>>n>>x>>y; dp[0][0]=1; for(int i=0;i<=x;i++) sum[0][i]=1; for(int i=1;i<=20;i++){ for(int j=1;j<=x;j++){ dp[i][j]=sum[i-1][j/2]; if(j>n) dp[i][j]=(dp[i][j]-sum[i-1][j-n-1]+MOD)%MOD; if(j-j/2<=y&&y<=j) dp[i][j]=(dp[i][j]-dp[i-1][j-y]+MOD)%MOD; sum[i][j]=(sum[i][j-1]+dp[i][j])%MOD; } } for(int i=0;i<=20;i++) ans=(ans+dp[i][x])%MOD; cout<<ans; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】