【学习笔记】20250806模拟赛

比赛链接

T1 一道复杂的构造题

注意到 n n n 的数据范围是大于 1 1 1 的,那么说明这个序列中一定有一个区间包含 1 1 1 ,那么这个区间的 g c d ( ) gcd() gcd() 就是 1 1 1 ,显然,当 m m m 为偶数时,无法构造。
m m m 分成若干个数的的 按位或 运算值,最简单的方案就是把 m m m 当做二进制,对于每一位的 1 1 1 分一组, m m m 最高位的 1 1 1 必须得小于等于 n n n,否则就无法构造。考虑如何快速求出一个数二进制最高位的值,不难想到最高位可以用 l o g 2 ( ) log_2() log2() 来求出:

int highbit(int x){
	return 1<<int(log2(x));
}

接下来我们遍历 j j j h i g h b i t ( m ) highbit(m) highbit(m) 1 1 1 ,再遍历 i i i 1 1 1 n n n ,把所有能被 j j j整除的 i i i 放到一组,时间复杂度为 O ( log ⁡ m × n ) O(\log{m}\times n) O(logm×n) ,轻松的过了这道题。

#include<bits/stdc++.h>
using namespace std;
int n,m;
queue<int>q;
int vis[100005];
int highbit(int x){
	return 1<<int(log2(x));
}
int main(){
	scanf("%d%d",&n,&m);
	if(highbit(m)>n || m%2==0) printf("-1");
	else{
		int j=highbit(m),k=m,cnt=0;;
		while(k>0){
			int sum=0;
			cnt++;
			for(int i=1;i<=n;i++){
				if(i%j==0 && vis[i]==0){
					sum++;
					vis[i]=1;
					printf("%d ",i);
				}
			}
			q.push(sum);
			k-=j;
			j=highbit(k);
		}
		printf("\n%d\n",cnt);
		int ls=0;
		while(!q.empty()){
			printf("%d %d\n",ls+1,ls+q.front());
			ls+=q.front();
			q.pop();
		}
		
	}
	return 0;
}

T2 茳侨串

题目要求将给定字符串变为恰好包含 3 3 3 个形如 01 01 01 10 10 10 的子串,那么这个字符串一定是 00 … 011 … 100 … 0 00\dots 011\dots 100\dots 0 000111000 11 … 100 … 011 … 1 11\dots 100\dots 011\dots 1 111000111,两种情况本质相同,下面就以第一种情况为例。
先考虑暴力做法,枚举 3 3 3 个端点,遍历每个区间,求出需要改的总数,时间复杂度 O ( n 3 ) O(n^3) O(n3),可以拿到 50 50 50 分。

for(int i=2;i<=n-1;i++){
	sum1=0x7f7f7f7f,sum2=0x7f7f7f7f;
	for(int j=1;j<i;j++){
		d=0;
		for(int k=1;k<=j;k++){
			if(a[k]!=0) d++;
		}
		for(int k=j+1;k<=i;k++){
			if(a[k]!=1) d++;
		}
		sum1=min(sum1,d);	
	}
	for(int j=i+1;j<n;j++){
		d=0;
		for(int k=i+1;k<=j;k++){
			if(a[k]!=0) d++;
		}
		for(int k=j+1;k<=n;k++){
			if(a[k]!=1) d++;
		}
		sum2=min(sum2,d);		
	}
	ans=min(ans,sum1+sum2);
}

对于区间 [ l , r ] [l,r] [l,r] 内要求有几个需要改的,若需都改成 1 1 1 ,那这段区间的区间和就得是 ( r − l + 1 ) (r-l+1) (rl+1) ,要改的数量就是这个结果和实际这段区间和的差;同理,若需都改成 0 0 0 ,那么要改的数量就是这段的区间和。如何维护区间和呢,用前缀和就可以了。此时的时间复杂度为 O ( N 2 ) O(N^2) O(N2),可以拿到 70 70 70 分。

for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
for(int i=2;i<=n-1;i++){
	sum1=0x7f7f7f7f,sum2=0x7f7f7f7f;
	for(int j=1;j<i;j++){
		sum1=min(sum1,2*s[j]+i-j-s[i]);	
	}
	for(int j=i+1;j<n;j++){
		sum2=min(sum2,2*s[j]+n-j-s[n]-s[i]);		
	}
	ans=min(ans,sum1+sum2);
}

我们发现对于这一种情况, i , s [ i ] , s [ n ] , n i,s[i],s[n],n i,s[i],s[n],n 都为定值,剩下的 2 × s [ j ] − j 2 \times s[j]-j 2×s[j]j可以用前缀最小值和后缀最小值直接求出,同理,第二种情况可以直接由前缀最大值和后缀最大值求出。经过这一次优化后,时间复杂度变为 O ( n ) O(n) O(n) 100 p t s 100pts 100pts 通过。

for(int i=0;i<=n+1;i++){
		smin[i]=tmin[i]=0x7f7f7f7f;
		smax[i]=tmax[i]=0x80808080;
	}
	for(int i=1;i<=n;i++){
		smin[i]=min(smin[i-1],t[i]);
		smax[i]=max(smax[i-1],t[i]);
	}
	for(int i=n-1;i>=1;i--){
		tmin[i]=min(tmin[i+1],t[i]);
		tmax[i]=max(tmax[i+1],t[i]);
	}
	int ans=0x7f7f7f7f;
	for(int i=2;i<n-1;i++){
		ans=min(ans,smin[i-1]+i-s[i]+tmin[i+1]+n-s[n]-s[i]);
		ans=min(ans,-smax[i-1]+s[i]-tmax[i+1]+s[i]-i+s[n]);
	}

T3 好路径数

T4 江桥游览花园

T5 环计数

T6 数字竞赛2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值