OIFHA 2026 模拟赛第五试


冲浪( \(surf\)


鲨鱼噶呜·古拉正在网上冲浪。她的冲浪路线可以用数轴来模拟,起点在\(1\)位置,终点在\(L\)位置。

当她在\(x\)位置时,如果跳跃能力是\(k\),她就能跳到\([x,x+k]\)内的任意整数位置。她的跳跃能力初值是\(1\)

不过冲浪路上可不平坦,有\(n\)个障碍区间,每个障碍用区间\([l,r]\)表示,意味着她不能到达\([l,r]\)区间内的任何位置。

路上还散落着\(m\)个能量道具。第\(i\)个道具位于\(x_i\)位置,能量为\(v_i\)。当古拉到达\(x_i\)位置时,可以选择拾取道具来增加\(v_i\)点跳跃能力。同一个位置可能有多个道具,她可以自由选择拾取或忽略任一道具。

请帮她算一下,最少需要收集多少个道具才能冲浪到终点\(L\)?如果无法抵达终点,请输出\(−1\)

数据范围

测试点 \(n\le\) \(m\le\) \(L\le\) 特殊性质
\(1 \sim 4\) \(10\) \(10\) \(10^9\)
\(5 \sim 7\) \(100\) \(10^5\) \(10^5\)
\(8 \sim 10\) \(10^5\) \(100\) \(10^7\)
\(11 \sim 13\) \(10^5\) \(10^5\) \(10^7\)
\(14 \sim 16\) \(10^5\) \(10^5\) \(10^9\) A
\(17 \sim 20\) \(10^5\) \(10^5\) \(10^9\)

特殊性质A:对\(\forall i\),有\(r_i+1<l_{i+1}\)

对于100%的数据,\(t \le 10,1 \le n,m \le 10^5,1\le \sum n,\sum m \le 2 \times 10^5,1 \le l_i,r_i,x_i < L \le 10^9\)


  1. 看数据范围可知:算法时间复杂度近似 \(O(n\log_2n)\)
  2. 路线遍历每一个,便可在 \(1~\) 此时位置的能量道具中任选。
  3. 大的能量道具优先选。
  4. 只有障碍长度大于跳跃能力才不可跳过。

由此可得解题方法。

  1. 将障碍和道具(可看作 \([x_{i},x_{i}]\) )按 \(l_{i}\)排序。
  2. 顺序遍历。
  3. 遇到道具便加入大根堆。
  4. 遇到障碍分两种情况。
    1. 跳跃能力够,直接忽略。
    2. 不够,不停从大根堆中弹出道具,直到跳跃能力够,累计上答案,再忽略。
  5. 输出答案。

本题 \(O(n\log_2n)\) 可解。


#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 500005
int t,n,m,l,jump,pd,cnt,nn;
priority_queue<int>q;
struct PT{int l,r;}p[N],pt[N]; 
bool cmp(PT a,PT b){return a.l<b.l;}
signed main(){
	freopen("surf.in","r",stdin);
	freopen("surf.out","w",stdout);
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld%lld",&n,&m,&l),jump=1,cnt=nn=0;
		for(int i=1;i<=n;i++) scanf("%lld%lld",&p[i].l,&p[i].r),p[i].r=min(p[i].r,l);
		for(int i=1;i<=m;i++) scanf("%lld%lld",&pt[i].l,&pt[i].r); 
		sort(p+1,p+n+1,cmp),sort(pt+1,pt+m+1,cmp);
		pd=(p[1].l!=1);
		for(int i=1;i<=n;i++){if(p[i].l<p[nn].r) p[nn].r=max(p[nn].r,p[i].r);else p[++nn]=p[i];}
		n=nn;
		while(!q.empty()) q.pop();
		for(int i=1,j=1;i<=n&&p[i].l<=l&&pd;i++){
			while(j<=m&&pt[j].l<p[i].l) q.push(pt[j].r),j++;
			while(j<=m&&pt[j].l<=p[i].r) ++j;
			while(jump<=p[i].r-p[i].l+1){
				if(q.empty()){pd=0;break;}
				jump+=q.top();q.pop(),cnt++;
			}
		}
		if(pd) printf("%lld\n",cnt);
		else printf("-1\n");
	}
	return 0;
} 

\(n\) 黄蛋( \(negg\)


给定 \(n\) 个同心圆环,环的编号从外向内依次增大,每个环从12点方向开始,被平均分成了 \(p_i\) 个区域,区域编号从12点方向开始,沿顺时针方向增大,第一个区域编号为\(1\)

同一个环内的相邻区域的交界处设有障碍,不可以穿越;而相邻的两个环之间的交界处没有障碍,可以穿越。记第 \(i\) 个环的第 \(j\) 个区域坐标为 \((i,j)\)。定义两个区域\(x\)\(y\)连通,当且仅当从\((i_x,j_x)\)出发,经过若干区域 \((i_{z_1} , j_{z_1})\)\((i_{z_2},j_{z_2})\) …… 后,能够抵达 \((i_{y},j_{y})\),中间经过的任意区域 \((i_{z_k},j_{z_k})\) 都必须满足 \(min(i_x,i_y)\le i_{z_k}\le max(i_x,i_y)\)

现给出 \(q\) 个操作,操作共有两种类型:对于第一种操作,给定两个区域坐标 \((i_x,j_x)\)\((i_y,j_y)\) ,请你判断这两个区域是否连通;对于第二种操作,将第 \(k\) 个环重新分割成 \(q\) 份。

说明/提示

请选择较快的输入输出方式。

数据范围:

数据点 \(n\le\) \(q\le\) \(p\le\) 特殊性质
\(1 \sim 4\) \(10^2\) \(10^2\) \(10^4\)
\(5 \sim 6\) \(10^5\) \(10^2\) \(10^7\)
\(7 \sim 8\) \(10^2\) \(10^5\) \(10^7\)
\(9 \sim 10\) \(10^6\) \(10^6\) \(10^9\) A、B
\(11 \sim 15\) \(10^6\) \(10^6\) \(10^9\) B
\(16 \sim 20\) \(10^6\) \(10^6\) \(10^9\)

特殊性质A:所有 \(p_i\) 满足 \(p_i=i+1\)

特殊性质B:保证不存在第二种操作

对于100%的数据:\(1\le T\le10\) , \(\sum n,\sum q\le 10^6\) , \(1\le p_i\le10^9\)\(1\le i_x , i_y\le n\)\(1\le j_x\le p_{i_x}\)\(1\le j_y\le p_{i_y}\)


  1. 看数据范围又可知:算法时间复杂度近似(就只能是!) \(O(n\log n)\)
  2. 分析相邻两个环在什么情况下不可穿越:在与 \(p_{i}\)\(p_{i+1}\) 相关时,只有 \(\frac{l}{p_{i}}=\frac{r}{p_{i+1}}\),即 \(r*p_{i}=l*p_{i+1}\),即 \(l\)\(\frac{p_{i}}{gcd(p_{i},p_{i+1})}\) 的倍数时不可穿越。
  3. 分析相邻 \(k\) 个环在什么情况下不可穿越:在与 \(p_{i}\)\(p_{j}\) 相关时,只有 \(\frac{l_{i}}{p_{i}}=\frac{l_{i+1}}{p_{i+1}}=...=\frac{l_{j}}{p_{j}}\),即 \(l\)\(\frac{p_{i}}{gcd(p_{i},p_{i+1},...,p_{j})}\) 的倍数时不可穿越,设 \(\frac{x}{gcd(p_{y},p_{y+1},...,p_{z})}\)\(g_{x,y,z}\)
  4. 分析两个区域 \((i_{x}, j_{x})\)\((i_{y}, j_{y})\) 在什么情况下可穿越:与 \(i_{x}\)\(i_{y}\) 相关,只有 \(\lfloor\frac{j_{x}}{g_{x,x,y}}\rfloor=\lfloor\frac{j_{y}}{g_{y,x,y}}\rfloor\) 的倍数时可穿越。
  5. 问题转移到如何 \(O(\log{n})\) 求出 \(gcd(p_{i},p_{i+1},...,p_{j})\):线段树大法好,能做到 \(O(\log n)\)

此题应该解了( \(?\) ),常数很大,可能要卡常,时间复杂度 \(O(n\log^n)\)

为什么这个时间复杂度不是 \(O(n\log^2 n)\)

  1. 对于修改操作,将值修改成 \(x\),则每进行 \(gcd\) 中的一次操作,即对于 \(gcd(x,y)\) 变成 \(gcd(y,x \bmod y)\),则 \(x\) 至少缩小为原本的 \(\frac{1}{2}\),所以修改操作时间复杂度为 \(O(\log n)\)
  2. 对于查询操作。设线段树查询操作中能便利到的 \(gcd\) 的值为 \(g_{1},g_{2},...,g_{k},\),其合并的顺序大概为 \(gcd(gcd(...gcd(gcd(g_{1},g_{2}),g_{3}),...),gcd(...gcd(gcd(g_{k},g_{k-1}),g_{k-2}),...))\)\(gcd(...gcd(gcd(g_{1},g_{2}),g_{3}),...)\) 合并的 \(gcd\) 的操作数最多为 \(\log{g_{1}}+log{n}\)\(gcd(...gcd(gcd(g_{k},g_{k-1}),g_{k-2}),...)\) 合并的 \(gcd\) 的操作数最多为 \(\log{g_{k}}+log{n}\),最后一次合并操作数最多为 \(\log{max(g_{1},g_{k})}\),总时间复杂度为 \(O(\log n)\) 级别的。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 2000005
int t,n,q,p[N],op,x,y,a,b,s[N<<2];
void build(int rt,int l,int r){
	if(l==r){s[rt]=p[l];return ;}
	int mid=(l+r)>>1;
	build(rt<<1,l,mid),build((rt<<1)+1,mid+1,r);
	s[rt]=__gcd(s[rt<<1],s[(rt<<1)+1]);
}
void change(int rt,int l,int r,int x,int c){
	if(l==r){s[rt]=c;return ;}
	int mid=(l+r)>>1;
	if(mid>=x) change(rt<<1,l,mid,x,c);
	else change((rt<<1)+1,mid+1,r,x,c);
	s[rt]=__gcd(s[rt<<1],s[(rt<<1)+1]);
}
int searchh(int rt,int l,int r,int ls,int rs){
	if(l==ls&&r==rs){return s[rt];}
	int mid=(l+r)>>1,ans=0;
	if(mid>=ls) ans=__gcd(ans,searchh(rt<<1,l,mid,ls,min(rs,mid)));
	if(mid<rs) ans=__gcd(ans,searchh((rt<<1)+1,mid+1,r,max(mid+1,ls),rs));
	return ans;
}
void solve(int x,int y,int a,int b){
	int g=searchh(1,1,n,min(x,a),max(x,a));
	if(y/(p[x]/g)+(y%(p[x]/g)!=0)==b/(p[a]/g)+(b%(p[a]/g)!=0))printf("Yes\n");
	else printf("No\n");
}
signed main(){
	freopen("negg.in","r",stdin);
	freopen("negg.out","w",stdout);
	scanf("%lld",&t);
	while(t--){
		scanf("%lld%lld",&n,&q);
		for(int i=1;i<=n;i++) scanf("%lld",&p[i]);
		build(1,1,n);
		while(q--){
			scanf("%lld",&op);
			if(op==1)scanf("%lld%lld%lld%lld",&x,&y,&a,&b),solve(x,y,a,b);
			else scanf("%lld%lld",&x,&y),change(1,1,n,x,y),p[x]=y;
		}
	}
	return 0;
}

萌树(\(cute\)


题目背景

树的根会变,但树依然挺立——你的逻辑也该如此坚韧。

题目描述

太宇得到了一棵树,它有\(n\)个节点。对于选定的根节点\(r\),太宇想要计算这棵树的可爱度。

考虑树中所有由\(k\)个不同节点组成的集合。对于每个这样的集合,计算在以\(r\)为根时的最近公共祖先(LCA),将所有得到的LCA节点编号求和,那么这棵树的可爱度就是\(f(r)\),即

\[f(r)=\sum_{x_1<x_2...<x_k}LCA(x_1,x_2...,x_k) \]

在发现了树的可爱度后,太宇对树的萌度产生了兴趣!萌度定义为:

\[\sum_{r=1}^{n}f(r)=f(1)+f(2)+⋯+f(n) \]

可惜,太宇在忙。请帮帮他,计算这棵树的萌度吧!

输入格式

输入格式第一行包含测试用例数量\(t\)

每个测试用例的第一行包含两个整数\(n\)\(k\)——树中顶点数和要选的不同节点数。

接下来 \(n−1\) 行描述树的边。每行包含两个整数 \(u\)\(v\) ,表示顶点 \(u\) 和顶点 \(v\) 之间有一条边。保证这些边构成一棵树。

输出样例

输出格式对于每个测试用例,输出一个整数表示萌度,由于答案可能很大,你需要将答案对998244353取模。

样例输入

3
2 2
1 2
5 3
1 2
1 3
1 4
1 5
6 3
1 2
1 3
2 4
2 5
3 6

样例输出

3
110
324

数据范围

测试点 \(n\le\) \(k\le\) 特殊性质
\(1 \sim 3\) \(12\) \(12\)
\(4 \sim 7\) \(2\times 10^3\) \(3\)
\(8 \sim 13\) \(2\times10^3\) \(2\times10^3\)
\(14 \sim 15\) \(2\times10^5\) \(2\times10^5\) A
\(16 \sim 17\) \(2\times10^5\) \(2\times10^5\) B
\(18 \sim 25\) \(2\times10^5\) \(2\times10^5\)

特殊性质A:对任意的\(i\),都有\(u_i=i,v_i=i+1\)

特殊性质B:对任意的\(i\),都有\(u_i=x,1\le x\le n\)

对于100%的数据,\(1 \le t \le 100 ,1 \le \sum n \le 2 \times 10^5,2 \le k \le n \le 2\times 10^5\)

样例说明

针对第二个样例,选择的3个结点分别为{1,2,3},在根节点为1,2,3,4,5的情况下,LCA(1,2,3)分别为1,2,3,1,1,和为8

{1,2,4}:1,2,1,4,1,和为9

{1,2,5}:1,2,1,1,5,和为10

{1,3,4}:1,1,3,4,1,和为10

{1,3,5}:1,1,3,1,5,和为11

{1,4,5}:1,1,1,4,5,和为12

{2,3,4}:1,2,3,4,1,和为11

{2,3,5}:1,2,3,1,5,和为12

{2,4,5}:1,2,1,4,5,和为13

{3,4,5}:1,1,3,4,5,和为14,总和为110。


看数据范围又可知:算法时间复杂度是 \(O(n)\),换根 \(dp\),解了,时间复杂度正确。


#include<bits/stdc++.h>
using namespace std;
#define int long long
#define N 200005
#define md 998244353
int t,n,k,head[N],tot=1,jc[N],nyjc[N],sz[N],ans,anss;
struct EDGE{int v,nt;}edge[N<<2]; 
void addedge(int x,int y){
	edge[tot].v=y,edge[tot].nt=head[x],head[x]=tot++;
	edge[tot].v=x,edge[tot].nt=head[y],head[y]=tot++;
}
void exgcd(int a,int b,int &x,int &y){
	if(b==0){x=1,y=0;return ;}
	exgcd(b,a%b,y,x);
	y-=a/b*x;
}
int ny(int w){
	int x,y;
	w=(w%md+md)%md;
	exgcd(w,md,x,y);
	return (x%md+md)%md;
} 
int c(int x,int y){
	if(x>y) return 0;
	return jc[y]*nyjc[x]%md*nyjc[y-x]%md;
}
void dfs(int x,int f){
	sz[x]=1; 
	for(int i=head[x];i;i=edge[i].nt){
		int y=edge[i].v;
		if(y==f) continue;
		dfs(y,x);
		if(sz[x]+sz[y]>=k) ans=(ans+(c(k,sz[x]+sz[y])-c(k,sz[y])-c(k,sz[x]))%md*x%md)%md;
		sz[x]+=sz[y];
	}
}
void dfss(int x,int f){
	ans=(ans+(c(k,n)-c(k,sz[x])-c(k,n-sz[x]))%md*x%md)%md;
	anss=(anss+ans)%md; 
	for(int i=head[x];i;i=edge[i].nt){
		int y=edge[i].v;
		if(y!=f) ans=(ans-(c(k,n)-c(k,sz[y])-c(k,n-sz[y]))%md*x%md)%md,dfss(y,x),ans=(ans+(c(k,n)-c(k,sz[y])-c(k,n-sz[y]))%md*x%md)%md;
	}
	ans=(ans-(c(k,n)-c(k,sz[x])-c(k,n-sz[x]))%md*x%md)%md;
}
signed main(){
	freopen("cute.in","r",stdin);
	freopen("cute.out","w",stdout);
	scanf("%lld",&t),jc[0]=nyjc[0]=1;
	for(int i=1;i<=N-5;i++) jc[i]=jc[i-1]*i%md;
	nyjc[N-5]=ny(jc[N-5]);
	for(int i=N-6;i;i--) nyjc[i]=nyjc[i+1]*(i+1)%md;
	while(t--){
		scanf("%lld%lld",&n,&k),ans=anss=0;
		memset(head,0,sizeof(head)),tot=1;
		for(int i=1,x,y;i<n;i++)cin>>x>>y,addedge(x,y);
		dfs(1,0),dfss(1,0);
		cout<<(anss+md)%md<<endl;
	}
	return 0;
}

\(T4\) 不会

posted on 2026-04-11 16:47  Ryan427  阅读(20)  评论(0)    收藏  举报