河北师范大学2026校赛题解(A,E,I)

题解

A-排序是你的谎言

预计难度:1900-2100

思路

如果直接排序维护 xxx 的位置,复杂度是完全不能接受的,由于只关心 xxx 的位置,我们可以将所有小于等于 xxx 的数视为 000 , 所有大于 xxx 的数视为 111 ,即
ai={0,ai≤x1,ai>x a_i= \begin{cases} 0,\quad a_i\leq x\\ 1, \quad a_i>x \end{cases} ai={0,ai≤x1,ai>x

记 000 的个数为 cnt0cnt0cnt0 , 111 的个数为 cnt1cnt1cnt1 , posxposxposx 为 xxx 的位置 ,对于 [l,r][l,r][l,r] 上的两种操作可以等价于:

  • 升序操作:将 [l,l+cnt0−1][l,l+cnt0-1][l,l+cnt0−1] 上的数变为 000 , 将 [l+cnt0,r][l+cnt0,r][l+cnt0,r] 上的数变为 111 , 若 posx∈[l,r]posx \in [l,r]posx∈[l,r] , 则操作之后 posx=l+cnt0−1posx=l+cnt0-1posx=l+cnt0−1
  • 降序操作: 将 [l,l+cnt1−1][l,l+cnt1-1][l,l+cnt1−1] 上的数变为 111 , 将 [l+cnt1,r][l+cnt1,r][l+cnt1,r] 上的数变为 111若 posx∈[l,r]posx \in [l,r]posx∈[l,r] , 则操作之后 posx=l+cnt1posx=l+cnt1posx=l+cnt1

显然 cnt1cnt1cnt1 可以通过区间求和得到, 使用线段树即可 O(logn)O(logn)O(logn) 完成每次区间求和与区间修改操作

代码

需要注意的地方是懒标记的初始值需要设为 −1-1−1 , 以及 updateupdateupdate 时可能出现 r<lr<lr<l 的情况

cpp 复制代码
#include<bits/stdc++.h>

#define ull unsigned long long 
#define ll long long
#define ld long double
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128

using namespace std;
const double eps=1e-6;
const int inf=1e9;
const ll INF=1e18;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));  

const int N=1e5+10;
int a[N];
int tr[N<<2];
int tag[N<<2];
int x;

void pushup(int p)
{
	tr[p]=tr[lc]+tr[rc];
	return;
}

void pushdown(int p,int l,int r)
{
	if(tag[p]==-1) return;
	int mid=(l+r)>>1;
	tr[lc]=(mid-l+1)*tag[p];
	tag[lc]=tag[p];
	tr[rc]=(r-mid)*tag[p];
	tag[rc]=tag[p];
	tag[p]=-1;
	return;
}

void build(int p,int l,int r)
{
	tag[p]=-1;
	if(l==r)
	{
		tr[p]=(a[l]<=x ? 0 : 1);
		return;
	}
	int mid=(l+r)>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(p);
}

void update(int p,int l,int r,int ql,int qr,int val)
{
	if(ql>qr) return;
	if(ql<=l && r<=qr)
	{
		tr[p]=(r-l+1)*val;
		tag[p]=val;
		return;
	}
	pushdown(p,l,r);
	int mid=(l+r)>>1;
	if(ql<=mid) update(lc,l,mid,ql,qr,val);
	if(qr>mid) update(rc,mid+1,r,ql,qr,val);
	pushup(p);
	return;
}

int query(int p,int l,int r,int ql,int qr)
{
	if(ql<=l && r<=qr) return tr[p];
	pushdown(p,l,r);
	int mid=(l+r)>>1;
	int sum=0;
	if(ql<=mid) sum+=query(lc,l,mid,ql,qr);
	if(qr>mid) sum+=query(rc,mid+1,r,ql,qr);
	return sum;
}


void solve()
{
	int n,m;
	cin>>n>>m>>x;
	int posx=0;
	for(int i=1;i<=n;i++) 
	{
		cin>>a[i];
		if(a[i]==x) posx=i;
	}
	
	build(1,1,n);
	
	for(int i=1;i<=m;i++)
	{
		int op,l,r;
		cin>>op>>l>>r;
		int cnt1=query(1,1,n,l,r);//>x
		int cnt0=r-l+1-cnt1;//<=x
		
		if(l<=posx && r>=posx)
		{
			if(op==0) posx=l+cnt0-1;
			else posx=l+cnt1;
		}
		
		if(op==0)
		{
			update(1,1,n,l,l+cnt0-1,0);
			update(1,1,n,l+cnt0,r,1);
		}
		else
		{
			update(1,1,n,l,l+cnt1-1,1);
			update(1,1,n,l+cnt1,r,0);
		}
		cout<<posx<<endl;
	}
}
	
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--)
	solve();
	
	return 0;
}

E-嗣源大定理

预计难度:1000~1200

灵感来源于某次在数学吧看到的一个帖子

思路1

由于 aia_iai 在变化中可能会变得很大,所以用筛法是不可行的,事实上由于 aia_iai 会很快收敛并循环,所以直接使用最简单的 O(n)O(\sqrt n)O(n ) 分解质因数去模拟即可通过,唯一需要注意的地方就是开 long long

事实上该题在预期中是第二个签到,但是只有很少人尝试了这道题

代码1

cpp 复制代码
#include<bits/stdc++.h>

#define ull unsigned long long 
#define ll long long
#define ld long double
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128

using namespace std;
const double eps=1e-6;
const int inf=1e9;
const ll INF=1e18;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));  

void solve()
{
	ll s,k;cin>>s>>k;
	
	auto get=[&](ll x)
	{
		ll mx=1;
		for(ll i=2;i*i<=x;i++)
		{
			if(x%i==0)
			{
				mx=i;
				while(x%i==0) x/=i;
			}
		}
		if(x>1) mx=x;
		return mx;
	};
	
	for(int i=1;i<=k;i++)
	{
		ll ne=get(s);
		s=ne*10+ne%10;
	}
	cout<<s<<endl;
}
	
	
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--)
	solve();
	
	return 0;
}

思路2

可以利用题面中提到的循环性质,只要得到了循环节的长度,就可以通过%运算快速计算出 aka_kak 的值,由于收敛的速度很快,前几项可以直接模拟得到,这种方法可以解决 kkk 达到 101210^{12}1012 时的数据

代码2

cpp 复制代码
#include<bits/stdc++.h>

#define ull unsigned long long 
#define ll long long
#define ld long double
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128

using namespace std;
const double eps=1e-6;
const int inf=1e9;
const ll INF=1e18;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));  

void solve()
{
	ll s,k;
	cin>>s>>k;
	
	map<ll,ll>vis;//第一次出现的位置
	vector<ll>path;//数列
	
	ll cur=s;
	path.pb(cur);
	vis[cur]=0;
	
	auto get=[&](ll x)
	{
		ll mx=1;
		for(ll i=2;i*i<=x;i++)
		{
			if(x%i==0)
			{
				mx=i;
				while(x%i==0) x/=i;
			}
		}
		if(x>1) mx=x;
		return mx;
	};
	
	auto f=[&](ll x)
	{
		ll p=get(x);
		return p*10+p%10;
	};
	
	for(ll i=1;i<=k;i++)
	{
		cur=f(cur);
		if(vis.find(cur)!=vis.end())
		{
			int len1=vis[cur];//第一次出现的位置
			int len2=i-len1;//循环节长度
			int pos=(k-i)%len2;
			cout<<path[len1+pos]<<endl;
			return;
		}
		vis[cur]=i;
		path.pb(cur);
	}
	cout<<cur<<endl;
}
	
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--)
	solve();
	
	return 0;
}

I-太阳:镜中的幻影

预计难度:1600~1800

思路

海平面 HHH 从高向低下降,当 H≤hi,jH \le h_{i,j}H≤hi,j 时格子变成陆地。这等价于:我们将所有格子按海拔高度 hi,jh_{i,j}hi,j 从大到小排序,依次将格子"激活"为陆地,使用并查集动态维护当前陆地组成的连通分量(岛屿)个数即可。

具体来说每激活一块陆地就将岛屿个数 cntcntcnt 加1 , 然后检查其四周的陆地,若其四周的陆地已经激活且与其属于不同集合则将 cntcntcnt 减1 , 若处理完一批相同高度的陆地之后 cnt=kcnt=kcnt=k 则当前高度为合法高度。由于要维护合法区间,我们还需要记录上一次的合法高度 last 以及判断上一次的高度是否处于合法区间之中,

  • 若 cnt=kcnt=kcnt=k 且之前高度不属于合法区间之内,说明当前高度为一个合法区间的右端点(上限)
  • 若 cnt!=kcnt!=kcnt!=k 且之前高度属于合法区间之内,说明当前高加1为上一个合法区间的左端点(下限)

需要注意的是若最后一个高度被处理完之后仍处在合法区间内(cnt始终为k),则说明该合法区间的左端点为1

代码

cpp 复制代码
#include<bits/stdc++.h>

#define ull unsigned long long 
#define ll long long
#define ld long double
#define lc p<<1
#define rc p<<1|1
#define endl '\n'
#define all(a) a.begin()+1,a.end()
#define all0(a) a.begin(),a.end()
#define lowbit(a) (a&-a)
#define fi first
#define se second
#define pb push_back
#define yes cout<<"YES"<<endl
#define no cout<<"NO"<<endl
#define i128 __int128

using namespace std;
const double eps=1e-6;
const int inf=1e9;
const ll INF=1e18;
typedef pair<int,int>PII;
typedef array<int,3>PIII;
mt19937_64 rnd(time(0));  

const int N=1e6+10;

int p[N];
int vis[N];

int find(int x)
{
	if(x!=p[x]) p[x]=find(p[x]);
	return p[x];
}

struct node{
	int h,x,y;
	bool operator < (const node &t)const
	{
		return h>t.h;
	}
};

int dx[]={0,1,0,-1};
int dy[]={-1,0,1,0};

void solve()
{
	int n,m,k;
	cin>>n>>m>>k;
	for(int i=1;i<=n*m;i++) {p[i]=i;vis[i]=0;}
	
    vector<node>a(n*m+1);
	int id=1;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++) 
		{
			int x;cin>>x;
			a[id++]={x,i,j};
		}
	}
	
	sort(all(a));
	int cnt=0;
	
	auto get=[&](int x,int y)
	{
		return (x-1)*m+y;
	};
	
	
	vector<PII>ans;
	int last=-1;
	int ok=0;
	
	for(int i=1;i<=n*m;)
	{
		int j=i;
		int curh=a[i].h;
		while(j<=n*m && a[j].h==curh) 
		{
			int pos=get(a[j].x,a[j].y);
			vis[pos]=1;
			cnt++;
			j++;
		}
		
		for(int t=i;t<j;t++)
		{
			int id1=get(a[t].x,a[t].y);
			for(int d=0;d<4;d++)
			{
				int nx=a[t].x+dx[d];
				int ny=a[t].y+dy[d];
				int id2=get(nx,ny);
				if(nx<1 || nx>n || ny<1 || ny>m) continue;
				if(!vis[id2]) continue;
				
				int px=find(id1);
				int py=find(id2);
				if(px!=py)
				{
					p[px]=py;
					cnt--;
				}
			}
		}
		
		if(cnt==k)
		{
			if(!ok) 
			{
				ok=1;
				last=curh;
			}
		} 
		else
		{
			if(ok)
			{
				ans.pb({curh+1,last});
				ok=0;
			}
		}
		i=j;
	}
	if(ok) ans.pb({1,last});
	if(!ans.size()) {cout<<-1<<endl;return;}
	
	sort(all0(ans));
	cout<<ans.size()<<endl;
	for(auto [l,r]:ans) cout<<l<<" "<<r<<endl;
}
	
	
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int t;cin>>t;
	while(t--)
	solve();
	
	return 0;
}
相关推荐
py有趣3 小时前
力扣热门100题之环形链表
算法·leetcode·链表
py有趣3 小时前
力扣热门100题之回文链表
算法·leetcode·链表
学嵌入式的小杨同学4 小时前
STM32 进阶封神之路(三十九)FreeRTOS 临界区、挂起 / 删除、钩子函数、调度底层原理|从应用到内核深度解析
c++·stm32·单片机·嵌入式硬件·mcu·硬件架构·pcb
oioihoii4 小时前
Cursor根本无法调试C++
开发语言·c++
月落归舟5 小时前
帮你从算法的角度来认识二叉树---(二)
算法·二叉树
SilentSlot5 小时前
【数据结构】Hash
数据结构·算法·哈希算法
是娇娇公主~6 小时前
Lambda表达式详解
数据结构·c++
leaves falling6 小时前
C++ string 类:从入门到模拟实现
开发语言·c++