2025省选模拟14
2025省选模拟14
题目来源: 2025多校冲刺省选模拟赛16

\(T1\) P1022. 世界⼤战 \(15pts\)
-
部分分
- \(15pts\) :\(O(n^{2})\) 预处理 \(O(1)\) 查询。
点击查看代码
ll f[2010][2010],c[2010]; int main() { #define Isaac #ifdef Isaac freopen("worldwar.in","r",stdin); freopen("worldwar.out","w",stdout); #endif ll n,m,q,x,y,i,j; scanf("%lld%lld",&n,&m); for(i=1;i<=m;i++) scanf("%lld",&c[i]); memset(f,0x3f,sizeof(f)); f[1][1]=0; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { f[i+1][j]=min(f[i+1][j],f[i][j]+c[j]); f[i][j+1]=min(f[i][j+1],f[i][j]+i*i); } } scanf("%lld",&q); for(i=1;i<=q;i++) { scanf("%lld%lld",&x,&y); printf("%lld\n",f[x][y]); } return 0; } -
正解
- 考虑从 \((1,1)\) 到 \((x,y)\) 中间经过的若干中转点 \((x_{1},y_{1}),(x_{2},y_{2}),\dots,(x_{k},y_{k})\) 的贡献。
- 对于一条竖-横-竖的路径 \((x_{1},y_{1}) \to (x,y_{1}) \to (x,y_{2}) \to (x_{2},y_{2})\) ,其代价为 \(x^{2}(y_{2}-y_{1})+c_{y_{1}}(x-x_{1})+c_{y_{2}}(x_{2}-x)=(y_{2}-y_{1})x^{2}+(c_{y_{2}}-c_{y_{1}})x+c_{y_{2}}x_{2}-c_{y_{1}}x_{1}\) ,其最小值在 \(\frac{c_{y_{2}}-c_{y_{1}}}{2(y_{2}-y_{1})}\) 时取到,具体取值因要求为正整数所以取 \(\max(\min(\operatorname{round}(\frac{c_{y_{2}}-c_{y_{1}}}{2(y_{2}-y_{1})}),1),n)\) 。
- 设 \(opt(y_{1},y_{2})\) 等于上式。根据调整法,升序枚举 \(y\) 的同时单调栈维护单调递增的 \(opt(y_{i-1},y_{i})\) ,预处理前缀和后二分得到答案。
- 特判 \(y=1\) 时的转移。
点击查看代码
ll c[200010],sum[200010],ans[200010],s[200010],opt[200010],n,top=0; vector<pair<ll,ll> >q[200010]; ll get_opt(ll u,ll v) { return min(max((ll)round((c[v]-c[u])/(v-u)/2.0),1ll),n); } int main() { // #define Isaac #ifdef Isaac freopen("worldwar.in","r",stdin); freopen("worldwar.out","w",stdout); #endif ll m,k,x,y,pos,i,j; scanf("%lld%lld",&n,&m); for(i=1;i<=m;i++) scanf("%lld",&c[i]); scanf("%lld",&k); for(i=1;i<=k;i++) { scanf("%lld%lld",&x,&y); if(y==1) ans[i]=c[1]*(x-1); else q[y].push_back(make_pair(x,i)); } top=s[1]=opt[1]=1; for(i=2;i<=m;i++) { while(top>=2&&opt[top]>=get_opt(s[top],i)) top--; pos=get_opt(s[top],i); top++; s[top]=i; opt[top]=pos; sum[top]=sum[top-1]+(pos-opt[top-1])*c[s[top-1]]+(i-s[top-1])*pos*pos; for(j=0;j<q[i].size();j++) { pos=upper_bound(opt+1,opt+1+top,q[i][j].first)-opt-1; ans[q[i][j].second]=sum[pos]+(q[i][j].first-opt[pos])*c[s[pos]]+(i-s[pos])*q[i][j].first*q[i][j].first; } } for(i=1;i<=k;i++) printf("%lld\n",ans[i]); return 0; }
\(T2\) P1023. 跳⽔ \(25pts\)
-
部分分
- \(Subtask1\) :枚举全排列。
- \(Subtask2\) :从 \(1\) 每次步长为 \(1\) 往下跳链直至 \(n\) 的父亲, \(n\) 以下的部分则步长为 \(2\) 地跳,特判链底。
- \(Subtask4\) :输出 \(1 \sim n\)。
因数据过水,代码实测可以通过 \(Subtask3\) 中完全二叉树的部分分。
点击查看代码
struct node { int nxt,to; }e[4000010]; int head[2000010],du[2000010],f[2000010],fa[2000010],siz[2000010],dep[2000010],son[2000010],top[2000010],a[2000010],cnt=0; void add(int u,int v) { cnt++; e[cnt]=(node){head[u],v}; head[u]=cnt; } void dfs1(int x,int father) { siz[x]=1; fa[x]=father; dep[x]=dep[father]+1; f[dep[x]]=x; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=father) { dfs1(e[i].to,x); siz[x]+=siz[e[i].to]; son[x]=(siz[e[i].to]>siz[son[x]])?e[i].to:son[x]; } } } void dfs2(int x,int id) { top[x]=id; if(son[x]!=0) { dfs2(son[x],id); for(int i=head[x];i!=0;i=e[i].nxt) if(e[i].to!=fa[x]&&e[i].to!=son[x]) dfs2(e[i].to,e[i].to); } } int lca(int u,int v) { while(top[u]!=top[v]) { if(dep[top[u]]>dep[top[v]]) u=fa[top[u]]; else v=fa[top[v]]; } return dep[u]<dep[v]?u:v; } int get_dis(int x,int y) { return dep[x]+dep[y]-2*dep[lca(x,y)]; } int main() { #define Isaac #ifdef Isaac freopen("jump.in","r",stdin); freopen("jump.out","w",stdout); #endif int n,u,v,flag=1,sum=0,i; scanf("%d",&n); for(i=1;i<=n-1;i++) { scanf("%d%d",&u,&v); du[u]++; du[v]++; add(u,v); add(v,u); } dfs1(1,0); dfs2(1,1); if(n<=12||du[1]==n-1) { for(i=1;i<=n;i++) a[i]=i; do { flag=1; for(i=2;i<=n&&flag==1;i++) flag&=(get_dis(a[i-1],a[i])<=2); if(flag==1) { for(i=1;i<=n;i++) printf("%d\n",a[i]); return 0; } }while(next_permutation(a+2,a+n)); printf("quake!\n"); } else { sum=0; flag=1; for(i=1;i<=n;i++) { sum+=(du[i]==1); flag&=(du[i]<=2); } if(sum==2&&flag==1) { for(i=1;i<=dep[n]-1;i++) printf("%d\n",f[i]); for(i=dep[n]+1;i<=n;i+=2) printf("%d\n",f[i]); if(i==n+1) printf("%d\n",f[n]); for(i=n-2;i>=dep[n];i-=2) printf("%d\n",f[i]); } else printf("quake!\n"); } return 0; } -
正解
- 考虑将 \(1 \sim n\) 这条根链上的点提出来单独考虑。划分成的若干个部分相互独立。
- 设 \(f_{x,0/1/2/3}\) 分别表示能否 \(x\) 进 \(x\) 出/ \(x\) 进 \(x\) 的儿子出/ \(x\) 的儿子进 \(x\) 的儿子出/ \(x\) 的儿子进 \(x\) 出。特别地,当儿子中只有一个非叶子节点时,令 \(f_{x,2}=f_{x,1}\) 。
- \(x\) 进 \(x\) 出,要求 \(x\) 必须是叶子节点。
- \(x\) 进 \(x\) 的儿子出,要求先到达 \(x\) ,然后到达儿子中的非叶子节点(如果有则必须只能有一个),最后跳到叶子节点。
- \(x\) 的儿子进 \(x\) 的儿子出,在判掉必须有两个非叶子节点后,要求先到达第一个非叶子节点,然后到达 \(x\) ,接着到达第二个非叶子节点,最后跳到叶子节点。
- \(x\) 的儿子进 \(x\) 出,要求先到达儿子中的叶子节点,再到达非叶子节点,最后跳到 \(x\) 。
- \(f_{x,1}\) 和 \(f_{x,3}\) 本质相同,只是构造方案时搜索顺序的差别,故可以合并成一种情况。
- 设 \(g_{x,0/1},x \in (1 \to n)\) 分别表示 \(x\) 的子树遍历完后能否停在 \(x\) / \(x\) 的儿子。
- 从 \(x\) 出要求 \(fa_{x}\) 能出且 \(x\) 能出或 \(fa_{x}\) 的其他儿子能出且 \(x\) 进 \(x\) 出。
- \(fa_{x}\) 能出且 \(x\) 进 \(x\) 出,从 \(fa_{x}\) 跳到 \(x\) 这个叶子节点。
- \(fa_{x}\) 能出且 \(x\) 的儿子进 \(x\) 出,从 \(fa_{x}\) 跳到 \(x\) 能进的儿子,再跳到 \(x\) 。
- \(fa_{x}\) 的儿子能出要求必须 \(x\) 进 \(x\) 出,否则就跳不出来了。
- \(x\) 的儿子出要求 \(fa_{x}\) 能出且 \(x\) 的儿子能出或 \(fa_{x}\) 的其他儿子能出且 \(x\) 进 \(x\) 的儿子出。
- \(fa_{x}\) 能出且 \(x\) 进 \(x\) 的儿子出,从 \(fa_{x}\) 跳到 \(x\) ,再跳到能出的儿子。
- \(fa_{x}\) 能出且 \(x\) 的儿子进 \(x\) 的儿子出,从 \(fa_{x}\) 跳到 \(x\) 能进的儿子,再跳到 \(x\) ,再跳到另一个能出的儿子。
- \(fa_{x}\) 的儿子能出且 \(x\) 进 \(x\) 的儿子出,从 \(fa_{x}\) 的儿子跳到 \(x\) ,再跳到能出的儿子。
- 从 \(x\) 出要求 \(fa_{x}\) 能出且 \(x\) 能出或 \(fa_{x}\) 的其他儿子能出且 \(x\) 进 \(x\) 出。
点击查看代码
struct node { int nxt,to; }e[4000010]; int head[2000010],f[3][2000010],g[2][2000010],vis[2000010],n,cnt=0; vector<int>s,d; void add(int u,int v) { cnt++; e[cnt]=(node){head[u],v}; head[u]=cnt; } void dfs(int x,int fa) { s.push_back(x); if(x==n) d=s; for(int i=head[x];i!=0;i=e[i].nxt) if(e[i].to!=fa) dfs(e[i].to,x); s.pop_back(); } void dp(int x,int fa) { int son=0,sum=0; f[0][x]=f[1][x]=f[2][x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0) { son++; dp(e[i].to,x); sum+=(f[0][e[i].to]==0&&f[1][e[i].to]==1); f[1][x]&=(f[0][e[i].to]|f[1][e[i].to]); f[2][x]&=(f[0][e[i].to]|f[1][e[i].to]); } } f[1][x]&=(sum<=1); f[2][x]&=(sum<=2); if(son==0) f[1][x]=f[2][x]=0; else f[0][x]=0; } void print_f(int x,int op,int fa) { if(op==0) printf("%d\n",x); if(op==1) { printf("%d\n",x); for(int i=head[x];i!=0;i=e[i].nxt) if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==0) print_f(e[i].to,3,x); for(int i=head[x];i!=0;i=e[i].nxt) if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==1) print_f(e[i].to,0,x); } if(op==2) { int sum=0; for(int i=head[x];i!=0;i=e[i].nxt) if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==0) sum++; if(sum==1) print_f(x,1,fa); else { for(int i=head[x];i!=0;i=e[i].nxt) { if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==0) { if(sum==2) { print_f(e[i].to,1,x); printf("%d\n",x); sum++; } else print_f(e[i].to,3,x); } } for(int i=head[x];i!=0;i=e[i].nxt) if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==1) print_f(e[i].to,0,x); } } if(op==3) { for(int i=head[x];i!=0;i=e[i].nxt) if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==1) print_f(e[i].to,0,x); for(int i=head[x];i!=0;i=e[i].nxt) if(e[i].to!=fa&&vis[e[i].to]==0&&f[0][e[i].to]==0) print_f(e[i].to,1,x); printf("%d\n",x); } } void print_g(int x,int op) { if(x==0) print_f(d[x],op,0); else { if(op==0) { if(g[0][x-1]==1&&f[0][d[x]]==1) { print_g(x-1,0); print_f(d[x],0,0); } else if(g[0][x-1]==1&&f[1][d[x]]==1) { print_g(x-1,0); print_f(d[x],3,0); } else { print_g(x-1,1); print_f(d[x],0,0); } } else { if(g[0][x-1]==1&&f[1][d[x]]==1) { print_g(x-1,0); print_f(d[x],1,0); } else if(g[0][x-1]==1&&f[2][d[x]]==1) { print_g(x-1,0); print_f(d[x],2,0); } else { print_g(x-1,1); print_f(d[x],1,0); } } } } int main() { #define Isaac #ifdef Isaac freopen("jump.in","r",stdin); freopen("jump.out","w",stdout); #endif int u,v,i; scanf("%d",&n); for(i=1;i<=n-1;i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } dfs(1,0); for(i=0;i<d.size();i++) vis[d[i]]=1; for(i=0;i<d.size();i++) dp(d[i],0); g[0][0]=f[0][1]; g[1][0]=f[1][1]; for(i=1;i<d.size();i++) { g[0][i]=(g[0][i-1]&(f[0][d[i]]|f[1][d[i]]))|(g[1][i-1]&f[0][d[i]]); g[1][i]=(g[0][i-1]&(f[1][d[i]]|f[2][d[i]]))|(g[1][i-1]&f[1][d[i]]); } if(g[0][d.size()-1]==1) print_g(d.size()-1,0); else printf("quake!\n"); return 0; }
\(T3\) P1024. 蛤蟆的近亲 \(40pts\)
-
先让 \(a_{i}\) 和 \(n+10\) 取 \(\min\) 缩小值域。
-
部分分
- \(40pts\) :\(O(n^{3})\) 预处理 \(O(1)\) 查询。
点击查看代码
int a[510],mex[510][510],w[510][510]; uint s[510][510]; bitset<100210>vis,f; int main() { #define Isaac #ifdef Isaac freopen("frog.in","r",stdin); freopen("frog.out","w",stdout); #endif int n,m,i,j,k; uint l,r,ans=0; scanf("%d%d",&n,&m); for(i=1;i<=n;i++) { scanf("%d",&a[i]); if(a[i]>=100010) a[i]=100010; } for(i=1;i<=n;i++) { vis.set(); for(j=i;j<=n;j++) { vis[a[j]]=0; mex[i][j]=vis._Find_first(); } } for(i=1;i<=n;i++) { f.reset(); for(j=i;j<=n;j++) { for(k=i;k<=j;k++) f[mex[k][j]]=1; w[i][j]=f.count(); } } for(i=n;i>=1;i--) { for(j=1;j<=n;j++) s[i][j]=s[i+1][j]+s[i][j-1]-s[i+1][j-1]+w[i][j]; } for(i=1;i<=m;i++) { scanf("%d%d",&l,&r); l=(l^ans)%n+1; r=(r^ans)%n+1; if(l>r) swap(l,r); ans=s[l][r]; printf("%u\n",ans); } return 0; } -
正解
- 建议先去温习 2024初三集训模拟测试3 T4 计蒜客 T3729 MEX 中 @APJifengc 写的题解。
总结
- \(T1\) \(c_{[2 \sim m]}\) 的部分分想简单了,以为只会存在两种走的方式。
- \(T2\)
- 为图方便把链底的特判和往下跳的过程合并在了一起
i=min(i+2,n)然后忘退出了,挂了 \(15pts\) 。 - 胡出来一个拆成入点和出点后跑路径匹配的有负圈的费用流(但全程流量为 \(1\) )做法,没时间再写了。
- 为图方便把链底的特判和往下跳的过程合并在了一起
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18732880,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。

浙公网安备 33010602011771号