多校A层冲刺NOIP2024模拟赛27终结篇
1.高一上十一月上旬日记2.NOIP2024加赛13.多校A层冲刺NOIP2024模拟赛184.NOIP2024加赛25.多校A层冲刺NOIP2024模拟赛196.多校A层冲刺NOIP2024模拟赛207.NOIP2024(欢乐)加赛38.高一上十一月中旬日记9.NOIP2024加赛410.多校A层冲刺NOIP2024模拟赛2111.2025--炼石计划-- 11 月 13 日 --NOIP 模拟赛 #2012.NOIP2024加赛513.NOIP2024加赛614.多校A层冲刺NOIP2024模拟赛2415.高一上十一月下旬日记16.多校A层冲刺NOIP2024模拟赛2517.NOIP2024加赛718.2025--炼石计划-- 11 月 23 日 --NOIP 模拟赛 #2319.【MX-S7】梦熊 NOIP 2024 模拟赛 3 & SMOI Round 2(同步赛)20.多校A层冲刺NOIP2024模拟赛2621.NOIP2024加赛8
22.多校A层冲刺NOIP2024模拟赛27终结篇
多校A层冲刺NOIP2024模拟赛27终结篇
A. 【模板】分治FFT
-
将式子展开后是一个形如
的形式。 -
考虑
如何转移。当我们选出一对 进行合并进入 的子问题,故 的系数为 ,边界为 。其中 表示 堆果子有多少种合并方式,即 。- 进一步可以得到
。
- 进一步可以得到
-
结论同 luogu P7950 [✗✓OI R1] 后方之水 。
点击查看代码
const ll p=998244353; ll a[100010],sum[100010],f[100010],g[100010]; int main() { freopen("fft.in","r",stdin); freopen("fft.out","w",stdout); ll n,ans=0,i,j; scanf("%lld",&n); for(i=1;i<=n;i++) { scanf("%lld",&a[i]); a[i]%=p; sum[i]=(sum[i-1]+a[i])%p; ans=(ans+a[i]*sum[i-1]%p)%p; } f[2]=g[2]=1; for(i=3;i<=n;i++) { f[i]=((i*(i-1)/2-1)%p*f[i-1]%p+g[i-1])%p; g[i]=i*(i-1)/2%p*g[i-1]%p; } printf("%lld\n",ans*f[n]%p); return 0; }
B. 【模板】最近公共祖先
-
通过数学直觉,得到边数为
的连通块的答案为 ,调整法即可证明。 -
当保证连通块是一棵树时,容易得到一种构造方式是从下往上考虑,对于
先尽可能让儿子之间一 为中转点互相连边,如果还有一条剩下的边那就传给 尝试与 的其他儿子连边。 -
当不保证连通块是一棵树时,过早地拿走一条边使得图不连通可能会导致不能全部取完,故仍考虑在
树上操作并延续树边的构造方式。对于非树边(前向边和返祖边)先进行内部互相连边,然后将余下的扔给 树上深度较浅的节点即可。点击查看代码
struct quality { int u,v,w; }; struct node { int nxt,to,id; }e[600010]; int head[300010],vis[300010],ins[300010],fa[300010],cnt=0; queue<int>q; vector<quality>ans; void add(int u,int v) { cnt++; e[cnt].nxt=head[u]; e[cnt].to=v; head[u]=cnt; } void dfs(int x) { vis[x]=ins[x]=1; for(int i=head[x];i!=0;i=e[i].nxt) { if(vis[e[i].to]==0) { dfs(e[i].to); } } for(int i=head[x];i!=0;i=e[i].nxt) { if(ins[e[i].to]==0)//非树边 { if(fa[e[i].to]==0)//尝试内部连边 { q.push(e[i].to); } else//直接连上 { ans.push_back((quality){x,e[i].to,fa[e[i].to]}); fa[e[i].to]=0; } } } while(q.size()>=2) { int tmp=q.front(); q.pop(); ans.push_back((quality){tmp,x,q.front()}); q.pop(); } if(q.empty()==0) { fa[x]=q.front(); q.pop(); } ins[x]=0; } int main() { freopen("lca.in","r",stdin); freopen("lca.out","w",stdout); int n,m,u,v,i; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d",&u,&v); add(u,v); add(v,u); } for(i=1;i<=n;i++) { if(vis[i]==0) { dfs(i); } } printf("%d\n",ans.size()); for(i=0;i<ans.size();i++) { printf("%d %d %d\n",ans[i].u,ans[i].v,ans[i].w); } return 0; }
C. 【模板】普通平衡树
-
部分分
- 测试点
:最长锯齿子序列可以 转移。 - 测试点
:答案只可能是 。
点击查看代码
int op[300010],l[300010],r[300010],x[300010],ans[300010]; vector<int>f[300010],g[300010]; void insert(int x,int y) { if(f[x].size()==0) { f[x].push_back(-1044266559); g[x].push_back(0x3f3f3f3f); f[x].push_back(y); g[x].push_back(y); ans[x]=1; } else { f[x].push_back(0x3f3f3f3f); g[x].push_back(-1044266559); for(int i=f[x].size()-1;i>=1;i--) { if(y<g[x][i-1]) { f[x][i]=min(g[x][i-1],y); } if(y>f[x][i-1]) { g[x][i]=max(g[x][i],y); } if(f[x][i]!=0x3f3f3f3f||g[x][i]!=-1044266559) { ans[x]=max(ans[x],i); } } ans[x]=max(ans[x],2); } } struct SMT { struct SegmentTree { int lazy; }tree[1200010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void update(int rt,int l,int r,int x,int y,int val) { if(x<=l&&r<=y) { tree[rt].lazy+=val; return; } int mid=(l+r)/2; if(x<=mid) { update(lson(rt),l,mid,x,y,val); } if(y>mid) { update(rson(rt),mid+1,r,x,y,val); } } int query(int rt,int l,int r,int pos) { if(l==r) { return tree[rt].lazy; } int mid=(l+r)/2; if(pos<=mid) { return query(lson(rt),l,mid,pos)+tree[rt].lazy; } else { return query(rson(rt),mid+1,r,pos)+tree[rt].lazy; } } }T; int main() { freopen("odt.in","r",stdin); freopen("odt.out","w",stdout); int n,m,flag1=1,flag2=1,cnt=0,i,j; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d",&op[i],&l[i]); if(op[i]==1) { scanf("%d%d",&r[i],&x[i]); cnt++; flag1&=(x[i]==cnt); flag2&=(x[i]==1000000000-cnt); } } if(flag1==1||flag2==1) { for(i=1;i<=m;i++) { if(op[i]==1) { T.update(1,1,n,l[i],r[i],1); } else { printf("%d\n",min(T.query(1,1,n,l[i]),2)); } } } else { for(j=1;j<=m;j++) { if(op[j]==1) { for(i=l[j];i<=r[j];i++) { insert(i,x[j]); } } else { printf("%d\n",ans[l[j]]); } } } return 0; }
- 测试点
-
正解
- 手摸
的情况后发现 即为所求,画在平面直角坐标系上取更新大小关系的截点加入答案即可。 - 线段树
pushdown
先天已经维护了时间的先后关系,故可以直接更新。
点击查看代码
int check(int a,int b,int c) { return (a>b&&b<c)||(a<b&&b>c); } struct SMT { struct node { int siz,ans,lc,zlc,rc,zrc; void clear() { siz=ans=lc=zlc=rc=zrc=0; } node operator + (const node &another) { node tmp; if(siz==0){return another;} if(another.siz==0){return *this;} tmp.siz=siz+another.siz; tmp.ans=ans+another.ans+(zrc!=0&&check(zrc,rc,another.lc))+(another.zlc!=0&&check(rc,another.lc,another.zlc)); tmp.lc=lc; tmp.zlc=(zlc!=0)?zlc:another.lc; tmp.rc=another.rc; tmp.zrc=(another.zrc!=0)?another.zrc:rc; return tmp; } }; struct SegmentTree { node lazy; }tree[1200010]; #define lson(rt) (rt<<1) #define rson(rt) (rt<<1|1) void pushdown(int rt) { tree[lson(rt)].lazy=tree[lson(rt)].lazy+tree[rt].lazy; tree[rson(rt)].lazy=tree[rson(rt)].lazy+tree[rt].lazy; tree[rt].lazy.clear(); } void update(int rt,int l,int r,int x,int y,int val) { if(x<=l&&r<=y) { tree[rt].lazy=tree[rt].lazy+(node){1,0,val,0,val,0}; return; } pushdown(rt); int mid=(l+r)/2; if(x<=mid) { update(lson(rt),l,mid,x,y,val); } if(y>mid) { update(rson(rt),mid+1,r,x,y,val); } } int query(int rt,int l,int r,int pos) { if(l==r) { return(tree[rt].lazy.siz>=3)?tree[rt].lazy.ans+2:tree[rt].lazy.siz; } pushdown(rt); int mid=(l+r)/2; if(pos<=mid) { return query(lson(rt),l,mid,pos); } else { return query(rson(rt),mid+1,r,pos); } } }T; int main() { freopen("odt.in","r",stdin); freopen("odt.out","w",stdout); int n,m,pd,l,r,x,i; scanf("%d%d",&n,&m); for(i=1;i<=m;i++) { scanf("%d%d",&pd,&l); if(pd==1) { scanf("%d%d",&r,&x); T.update(1,1,n,l,r,x); } else { printf("%d\n",T.query(1,1,n,l)); } } return 0; }
- 手摸
D. 【模板】平面最近点对
-
等价于最小化
维上最大的极差。 -
部分分
- 测试点
:爆搜。
点击查看代码
int a[16010][25],ans=0x7f7f7f7f; vector<int>state; void dfs(int pos,int n,int k) { if(pos==n+1) { int sum=0; for(int i=1;i<=k;i++) { int maxx=0,minn=0x7f7f7f7f; for(int j=0;j<state.size();j++) { maxx=max(maxx,a[state[j]][i]); minn=min(minn,a[state[j]][i]); } sum=max(sum,maxx-minn); } ans=min(ans,sum); } else { state.push_back(pos*2); dfs(pos+1,n,k); state.pop_back(); state.push_back(pos*2+1); dfs(pos+1,n,k); state.pop_back(); } } int main() { freopen("pair.in","r",stdin); freopen("pair.out","w",stdout); int n,k,i,j; scanf("%d%d",&n,&k); for(i=1;i<=n;i++) { for(j=1;j<=k;j++) { scanf("%d",&a[i*2][j]); } for(j=1;j<=k;j++) { scanf("%d",&a[i*2+1][j]); } } dfs(1,n,k); printf("%d\n",ans); return 0; }
- 测试点
-
正解
- 同 [ARC069F] Flags ,考虑二分答案后
进行 。 - 每一维相互独立,线段树优化建图常数巨大且难写,观察到升序排序后连边都是一个点向一段前、后缀连边的形式,写个前后缀优化建图即可。
点击查看代码
int dfn[680010],low[680010],ins[680010],col[680010],tot=0,scc_cnt=0,id; pair<int,int>a[25][16010]; vector<int>e[680010]; stack<int>s; void add(int u,int v) { e[u].push_back(v); } int rev(int x,int n) { return x<=n?x+n:x-n; } void tarjan(int x) { tot++; dfn[x]=low[x]=tot; s.push(x); ins[x]=1; for(int i=0;i<e[x].size();i++) { if(dfn[e[x][i]]==0) { tarjan(e[x][i]); low[x]=min(low[x],low[e[x][i]]); } else if(ins[e[x][i]]==1) low[x]=min(low[x],dfn[e[x][i]]); } if(dfn[x]==low[x]) { scc_cnt++; int tmp=0; while(tmp!=x) { tmp=s.top(); s.pop(); ins[tmp]=0; col[tmp]=scc_cnt; } } } bool check(int mid,int n,int k) { while(s.empty()==0) s.pop(); for(int i=1;i<=id;i++) { e[i].clear(); dfn[i]=low[i]=ins[i]=col[i]=0; } tot=scc_cnt=0; id=2*n; int pre,suf; for(int i=1;i<=k;i++) { id++; pre=id; for(int l=1,r=1;r<=2*n;r++) { for(;a[i][r].first-a[i][l].first>mid;l++) { id++; pre=id; add(pre,pre-1); add(pre,rev(a[i][l].second,n)); } if(l!=1) add(a[i][r].second,pre); } id++; suf=id; for(int l=2*n,r=2*n;l>=1;l--) { for(;a[i][r].first-a[i][l].first>mid;r--) { id++; suf=id; add(suf,suf-1); add(suf,rev(a[i][r].second,n)); } if(r!=2*n) add(a[i][l].second,suf); } } for(int i=1;i<=id;i++) if(dfn[i]==0) tarjan(i); for(int i=1;i<=n;i++) if(col[i]==col[i+n]) return false; return true; } int main() { #define Isaac #ifdef Isaac freopen("pair.in","r",stdin); freopen("pair.out","w",stdout); #endif int n,k,l=0,r=0x3f3f3f3f,mid,ans,i,j; scanf("%d%d",&n,&k); for(i=1;i<=n;i++) { for(j=1;j<=k;j++) { scanf("%d",&a[j][i].first); a[j][i].second=i; } for(j=1;j<=k;j++) { scanf("%d",&a[j][i+n].first); a[j][i+n].second=i+n; } } for(i=1;i<=k;i++) sort(a[i]+1,a[i]+1+2*n); while(l<=r) { mid=(l+r)/2; if(check(mid,n,k)==true) { ans=mid; r=mid-1; } else l=mid+1; } printf("%d\n",ans); return 0; }
- 同 [ARC069F] Flags ,考虑二分答案后
总结
误直接把 写成了 ,挂了 。 没去想树的部分分,只写了个乱搞做法。- 特殊性质
打假了,挂了 。 - 赛时以为线段树
pushdown
不能很好地维护插入数的先后关系,说明对pushdown
更新过程的认识不清。
- 特殊性质
又被卡在对高维空间的理解上,但因为有样例解释很快就理解了。
后记
下发了checker.exe
,但 下的一开始有点问题,中途又更换了两次。
本文来自博客园,作者:hzoi_Shadow,原文链接:https://www.cnblogs.com/The-Shadow-Dragon/p/18574508,未经允许严禁转载。
版权声明:本作品采用 「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0) 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具