2024初三集训模拟测试2
2024初三集训模拟测试2
\(T0\) 谜之阶乘 \(100pts\)
- 详见 普及模拟2 T4 阶乘 。
\(T1\) 小P的2048 \(10pts\)
-
大模拟,没什么好说的。
- 注意可以同时合并多对数字,但不能连续合并。
点击查看代码
ll a[10][10]; queue<ll>q; int main() { ll n,m,x1,y1,v1,x2,y2,v2,d,k,v,sum=0,ans=0,r,num,flag,i,j,h; cin>>n>>m>>x1>>y1>>v1>>x2>>y2>>v2; a[x1][y1]=v1; a[x2][y2]=v2; for(h=1;h<=m;h++) { cin>>d>>k>>v; r=num=flag=0; if(d==0) { for(j=1;j<=n;j++) { for(i=1;i<=n;i++) { if(a[i][j]!=0) { if(1<=i-1&&a[i-1][j]==0) { flag=1; } q.push(a[i][j]); } } for(i=1;i<=n;i++) { a[i][j]=(q.empty()==0)?q.front():0; if(q.empty()==0) { q.pop(); } } } for(j=1;j<=n;j++) { for(i=1;i<=n;i++) { if(i+1<=n&&a[i][j]==a[i+1][j]&&a[i][j]!=0) { flag=1; a[i][j]*=2; a[i+1][j]=0; ans+=a[i][j]; } } } for(j=1;j<=n;j++) { for(i=1;i<=n;i++) { if(a[i][j]!=0) { if(1<=i-1&&a[i-1][j]==0) { flag=1; } q.push(a[i][j]); } } for(i=1;i<=n;i++) { a[i][j]=(q.empty()==0)?q.front():0; if(q.empty()==0) { q.pop(); } r+=(a[i][j]==0); } } } if(d==1) { for(j=1;j<=n;j++) { for(i=n;i>=1;i--) { if(a[i][j]!=0) { if(i+1<=n&&a[i+1][j]==0) { flag=1; } q.push(a[i][j]); } } for(i=n;i>=1;i--) { a[i][j]=(q.empty()==0)?q.front():0; if(q.empty()==0) { q.pop(); } } } for(j=1;j<=n;j++) { for(i=n;i>=1;i--) { if(1<=i-1&&a[i][j]==a[i-1][j]&&a[i][j]!=0) { flag=1; a[i][j]*=2; a[i-1][j]=0; ans+=a[i][j]; } } } for(j=1;j<=n;j++) { for(i=n;i>=1;i--) { if(a[i][j]!=0) { if(i+1<=n&&a[i+1][j]==0) { flag=1; } q.push(a[i][j]); } } for(i=n;i>=1;i--) { a[i][j]=(q.empty()==0)?q.front():0; if(q.empty()==0) { q.pop(); } r+=(a[i][j]==0); } } } if(d==2) { for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { if(a[i][j]!=0) { if(1<=j-1&&a[i][j-1]==0) { flag=1; } q.push(a[i][j]); } } for(j=1;j<=n;j++) { a[i][j]=(q.empty()==0)?q.front():0; if(q.empty()==0) { q.pop(); } } } for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { if(j+1<=n&&a[i][j]==a[i][j+1]&&a[i][j]!=0) { flag=1; a[i][j]*=2; a[i][j+1]=0; ans+=a[i][j]; } } } for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { if(a[i][j]!=0) { if(1<=j-1&&a[i][j-1]==0) { flag=1; } q.push(a[i][j]); } } for(j=1;j<=n;j++) { a[i][j]=(q.empty()==0)?q.front():0; if(q.empty()==0) { q.pop(); } r+=(a[i][j]==0); } } } if(d==3) { for(i=1;i<=n;i++) { for(j=n;j>=1;j--) { if(a[i][j]!=0) { if(j+1<=n&&a[i][j+1]==0) { flag=1; } q.push(a[i][j]); } } for(j=n;j>=1;j--) { a[i][j]=(q.empty()==0)?q.front():0; if(q.empty()==0) { q.pop(); } } } for(i=1;i<=n;i++) { for(j=n;j>=1;j--) { if(1<=j-1&&a[i][j]==a[i][j-1]&&a[i][j]!=0) { flag=1; a[i][j]*=2; a[i][j-1]=0; ans+=a[i][j]; } } } for(i=1;i<=n;i++) { for(j=n;j>=1;j--) { if(a[i][j]!=0) { if(j+1<=n&&a[i][j+1]==0) { flag=1; } q.push(a[i][j]); } } for(j=n;j>=1;j--) { a[i][j]=(q.empty()==0)?q.front():0; if(q.empty()==0) { q.pop(); } r+=(a[i][j]==0); } } } if(flag==1&&r!=0) { sum++; for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { num+=(a[i][j]==0); if(num==k%r+1) { a[i][j]=v; break; } } if(num==k%r+1) { break; } } } else { break; } } cout<<sum<<endl; cout<<ans<<endl; return 0; }
\(T2\) 子集 \(0pts\)
-
部分分
-
\(20 \%\) :由于 \(\frac{n}{k}\) 为偶数,故在待选递增序列中左右端各依次取 \(\frac{n}{2k}\) 个数即可。
点击查看代码
if((n/k)%2==0) { l=0; r=n+1; cout<<"Yes"<<endl; for(i=1;i<=k;i++) { for(j=1;j<=(n/k)/2;j++) { l++; cout<<l<<" "; r--; cout<<r<<" "; } cout<<endl; } }
-
-
正解
- 当 \(n=k \ne 1\) 或 \(\frac{n(n+1)}{2} \not\equiv 0 \pmod{k}\) 时,无解。
- 当 \(k=1\) 时,依次输出 \(1 \sim n\) 即可。
- 当 \(\frac{n}{k}\) 为偶数时,在待选递增序列中左右端各依次取 \(\frac{n}{2k}\) 个数即可。
- 当 \(\frac{n}{k}\) 为奇数时,考虑尽可能减少因 \(\frac{n}{k}\) 为奇数而对答案产生的影响,这就要求我们尽可能缩小 \(\frac{n}{k}\) 的范围。为方便代码书写,我们选择每个子集中特殊构造 \(3\) 个数,剩下的 \(\frac{n}{k}-3\) 个数处理方式同 \(\frac{n}{k}\) 为偶数时的构造方式。具体地,我们单独选出 \(1 \sim 3k\) 来进行特殊构造。此时问题转化为了 \(3k\) 个连续的自然数如何将其分为 \(k\) 组,每组恰好只有 \(3\) 个数,且每组内数的总和相等。其中一种构造方式为 \(\begin{cases} (1 \le i \le \frac{k-1}{2} ): i+(\frac{3k+1}{2}+i)+(3k+1-2i)=\frac{3(3k+1)}{2} \\ (\frac{k+1}{2} \le i \le k): i+(\frac{k+1}{2}+i)+(4k+1-2i)=\frac{3(3k+1)}{2} \end{cases}\) 。
点击查看代码
int main() { ll t,n,k,i,j,l,r,h; cin>>t; for(h=1;h<=t;h++) { cin>>n>>k; if((n!=1&&n==k)||(n*(n+1)/2)%k!=0) { cout<<"No"<<endl; } else { cout<<"Yes"<<endl; if(k==1) { for(i=1;i<=n;i++) { cout<<i<<" "; } cout<<endl; } else { if((n/k)%2==0) { l=0; r=n+1; for(i=1;i<=k;i++) { for(j=1;j<=(n/k)/2;j++) { l++; cout<<l<<" "; r--; cout<<r<<" "; } cout<<endl; } } else { l=3*k; r=n+1; for(i=1;i<=(k-1)/2;i++) { for(j=1;j<=(n/k-3)/2;j++) { l++; cout<<l<<" "; r--; cout<<r<<" "; } cout<<i<<" "<<(3*k+1)/2+i<<" "<<3*k+1-2*i<<endl; } for(i=(k+1)/2;i<=k;i++) { for(j=1;j<=(n/k-3)/2;j++) { l++; cout<<l<<" "; r--; cout<<r<<" "; } cout<<i<<" "<<(k+1)/2+i<<" "<<4*k+1-2*i<<endl; } } } } } return 0; }
\(T3\) 混凝土粉末 \(0pts\)
- 部分分
- \(84pts\)
-
在线做法
- 询问的本质是对之前的所有修改操作进行修改,找到第一个所得高度 \(\ge y\) 的位置的时间,若没有找到则为 \(0\) 。
- 枚举并统计之前的所有修改操作对当前查询的影响即可。
- 最坏时间复杂度为 \(O(q^2)\) ,带 \(\frac{1}{4}\) 的常数。
点击查看代码
struct node { ll l,r,h,id; }a[1000001]; int main() { ll n,q,m=0,pd,x,y,sum,ans,i,j; cin>>n>>q; for(i=1;i<=q;i++) { cin>>pd; if(pd==1) { m++; cin>>a[m].l>>a[m].r>>a[m].h; a[m].id=i; } else { cin>>x>>y; sum=ans=0; for(j=1;j<=m;j++) { if(a[j].l<=x&&x<=a[j].r) { sum+=a[j].h; if(sum>=y) { ans=a[j].id; break; } } } cout<<ans<<endl; } } return 0; }
-
- \(84pts\)
- 正解
- 离线做法
- 考虑优化暴力的在线做法。将所有操作进行离线下来,同样统计之前的所有修改操作对当前查询的影响。
- 二分树状数组
-
树状数组记录每个点每个时刻的高度,然后二分即可。用扫描线的思想将二维树状数组压成一维数组。具体地,对于一段修改的区间 \([l_{i},r_{i}]\) ,当扫描到 \(l\) 时,在 \(l\) 位置上加上 \(h_{i}\) ,增加影响;当扫描到 \(r+1\) 时,在 \(r+1\) 位置上减去 \(h_{i}\) ,消除影响。
点击查看代码
ll pd[2000001],c[2000001],ans[2000001]; vector<pair<ll,ll> >a[2000001],ask[2000001]; ll lowbit(ll x) { return (x&(-x)); } void add(ll n,ll x,ll key) { for(ll i=x;i<=n;i+=lowbit(i)) { c[i]+=key; } } ll getsum(ll x) { ll ans=0; for(ll i=x;i>=1;i-=lowbit(i)) { ans+=c[i]; } return ans; } int main() { ll n,q,l,r,mid,h,i,j; cin>>n>>q; for(i=1;i<=q;i++) { cin>>pd[i]>>l>>r; if(pd[i]==1) { cin>>h; a[l].push_back(make_pair(i,h)); a[r+1].push_back(make_pair(i,-h)); } else { ask[l].push_back(make_pair(i,r)); } } for(i=1;i<=n;i++) { for(j=0;j<a[i].size();j++) { add(q,a[i][j].first,a[i][j].second); } for(j=0;j<ask[i].size();j++) { l=1; r=ask[i][j].first; ans[ask[i][j].first]=0; while(l<=r) { mid=(l+r)/2; if(getsum(mid)<ask[i][j].second) { l=mid+1; } else { ans[ask[i][j].first]=mid; r=mid-1; } } } } for(i=1;i<=q;i++) { if(pd[i]==2) { cout<<ans[i]<<endl; } } return 0; }
-
- 树状数组或线段树上二分
- 整体二分
- 二分树状数组
- 考虑优化暴力的在线做法。将所有操作进行离线下来,同样统计之前的所有修改操作对当前查询的影响。
- 在线做法
- 主席树
- 离线做法
\(T4\) 排水系统 \(0pts\)
-
部分分
- \(7pts\) :特判输出样例 \(1,3\) 。
-
正解
- 设 \(f_{i}\) 表示在不考虑堵塞情况下,经过 \(i\) 点的污水总吨数。
- 由于若 \((x,y)\) 堵塞,经过 \(x\) 点经过的污水总吨数不变,故当 \((x,y)\) 堵塞时,经过 \(y\) 点的污水总吨数减少了 \(\frac{f_{x}}{dout_{x}}\) ,经过 \(x\) 点的其他子节点的污水总吨数增加了 \(\frac{f_{x}}{dout_{x}-1} - \frac{f_{x}}{dout_{x}}= \frac{f_{x}}{dout_{x}(dout_{x}-1)}\) 。将 \(-\frac{f_{x}}{dout_{x}}\) 和 \(\frac{f_{x}}{dout_{x}(dout_{x}-1)}\) 放在原图上,计算出原始期望总吨数,可以得到新的 \(f\) 。
- 但枚举 \(x\) 点的其他子节点时间复杂度会爆炸,但 \(x\) 点的其他子节点每个增加 \(\frac{f_{x}}{dout_{x}(dout_{x}-1)}\) 可以转化为从 \(x\) 点增加 \(\frac{f_{x}}{dout_{x}(dout_{x}-1)} \times dout_{x}=\frac{f_{x}}{dout_{x}-1}\) , \(y\) 点减少了 \(\frac{f_{x}}{dout_{x}}+\frac{f_{x}}{dout_{x}(dout_{x}-1)}=\frac{f_{x}}{dout_{x}-1}\) ,然后再转移到 \(x\) 点的所有子节点。
- 此时忽略了 \((x,y)\) 的堵塞,因为从 \(x\) 流向 \(y\) 的一部分弥补了 \(y\) 少的部分。
点击查看代码
const ll p=998244353; struct node { ll nxt,to,w; }e[500000]; ll head[500000],f[2][500000],dout[500000],din[3][500000],cnt=0; ll qpow(ll a,ll b,ll p) { ll ans=1; while(b>0) { if(b&1) { ans=ans*a%p; } b>>=1; a=a*a%p; } return ans; } void add(ll u,ll v,ll w) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; e[cnt].w=w; head[u]=cnt; } void top_sort1(ll n,ll din[],ll f[]) { queue<ll>q; ll x,i,ls; for(i=1;i<=n;i++) { if(din[i]==0) { q.push(i); } } while(q.empty()==0) { x=q.front(); q.pop(); ls=f[x]*qpow(dout[x],p-2,p)%p; for(i=head[x];i!=0;i=e[i].nxt) { din[e[i].to]--; f[e[i].to]=(f[e[i].to]+ls)%p; if(din[e[i].to]==0) { q.push(e[i].to); } } } } void top_sort2(ll n,ll din[],ll sum) { queue<ll>q; ll x,i,ls,inv=qpow(sum,p-2,p); for(i=1;i<=n;i++) { if(din[i]==0) { q.push(i); } } while(q.empty()==0) { x=q.front(); q.pop(); ls=f[0][x]*qpow(dout[x]-1,p-2,p)%p; for(i=head[x];i!=0;i=e[i].nxt) { din[e[i].to]--; f[1][x]=(f[1][x]+(inv*e[i].w%p)*ls%p)%p; f[1][e[i].to]=(f[1][e[i].to]-(inv*e[i].w%p)*ls%p+p)%p; if(din[e[i].to]==0) { q.push(e[i].to); } } } } int main() { ll n,m,r,k,u,v,w,sum=0,i; cin>>n>>m>>r>>k; for(i=1;i<=k;i++) { cin>>u>>v>>w; add(u,v,w); dout[u]++; din[0][v]++; din[1][v]++; din[2][v]++; sum=(sum+w)%p; } for(i=1;i<=n;i++) { if(din[0][i]==0) { f[0][i]=1; } } top_sort1(n,din[0],f[0]); for(i=1;i<=n;i++) { if(din[1][i]==0) { f[1][i]=1; } } top_sort2(n,din[1],sum); top_sort1(n,din[2],f[1]); for(i=1;i<=n;i++) { if(dout[i]==0) { cout<<f[1][i]<<" "; } } return 0; }
总结
- 开题顺序 \(T0,T3,T2,T1\) 。
- \(T2\) 赛时没有删
freopen
,挂了 \(25pts\) 。 - \(T3\) 用的
gp_hash_table
,空间爆了。 - \(T1\) 写到比赛结束一个多小时也没有调完,很久没有写过大模拟了,该练练了。
- 菜就多练。
- \(T4\) 看见概率期望就直接跳了,也没时间写了。
后记
- \(T0\) 为原本的 \(T1\) ,因机房人做过的太多所以仅存在了 \(90s\) 就被换下去了,赛时只有我交了一份 \(AC\) 代码上去。
- \(T2\) 下发了
Special Judge
,还教了怎么使用Special Judge
,好评。 - \(T3\) 在没有提前说明的情况下使用了捆绑测试。
- \(PDF\) 上写了文件输入输出,但评测时却是标准输入输出。
- 有大样例,好评。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18021956,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。