蓝桥 周赛27 20250402

蓝桥 周赛27 20250402

https://www.lanqiao.cn/oj-contest/senior-27/

A:

题目大意:计算 \(\sum_{i=1}^{16}i\)

void solve(){
	cout<<(1+16)*16/2;
}

签到

B:

题目大意:给定字符串 \(s\) ,求子串 lan 的个数

void solve(){
	string s;
	cin>>s;
	LL l=0,la=0,lan=0;
	for (int i=0;i<s.size();i++){
		if (s[i]=='l')
			l++;
		if (s[i]=='a')
			la+=l;
		if (s[i]=='n')
			lan+=la;
	}
	cout<<lan;
}

签到,线性DP

C:

题目大意:

struct nd{
	LL a,b;
};

void solve(){
	int n;
	cin>>n;
	vector<nd> x(n+10);
	for (int i=1;i<=n;i++) cin>>x[i].a>>x[i].b;
	sort(x.begin()+1,x.begin()+n+1,[](nd u,nd v){return u.a-u.b>v.a-v.b;});
	LL sum=0;
	for (int i=1;i<=n;i++){
		if (i<=n/2) sum+=x[i].a;
		else sum+=x[i].b;
	}
	cout<<sum;
}

一眼看过去以为是背包问题,其实可以模拟 \(O(n)\) 计算

对于每个酬劳而言,最优选择是选择 \(A_i,B_i\) 中最大的,所以对每个酬劳按照 \(A_i-B_i\) 排序,先让小红选

贪心的选择前 \(\lfloor n/2\rfloor\) 个酬劳的 \(A_i\) ,一定是最优解

D:

题目大意:

void solve(){
	int n;
	cin>>n;
	vector<LL> a(n+10),b(n+10);
	for (int i=1;i<=n;i++) cin>>b[i];
	for (int i=1;i<=n;i++) cin>>a[i];
	sort(b.begin()+1,b.begin()+n+1);
	LL ans=0,sum=0;
	for (int i=1;i<=n;i++)
		ans+=b.begin()+n+1-upper_bound(b.begin()+i,b.begin()+n+1,a[i]);
	cout<<ans;
}

有点神经,先输入的是蓝队而不是红队

因为红队队员按照给定的顺序出场,而我们可以自由选择蓝队成员出场,并且我们当前出场的蓝队队员只要比一个红队出场的队员大,那么胜场加一,每次上场的蓝队队员需要和每一个红队队员进行比较

所以,蓝队的最优策略是将较大的队员后上场,可以这样考虑:越后上场的蓝队队员能和越多的红队队员进行比较,那么就有越大的胜利场次,而将最大的队员放在最后可以最大程度的增加胜算

将蓝队队员按照大小进行排序后,考虑每个红队队员 \(a_i\) ,他只能被他之后上场或同时上场的蓝队队员赢

那么从当前红队队员上场编号开始进行二分,用 upper_bound 找到 \(i\) 之后的大于 \(a_i\)\(b_j\) 的位置,从这个 \(b_j\) 开始,之后的 \(b_k\) 一定比 \(a_i\) 都要大,那么胜场数为 \(n-j+1\)

E:

题目大意:

const int mod=1e9+7;

void solve(){
	int n,d;
	cin>>n>>d;
	unordered_map<int,vector<int>> mp;
	for (int i=1;i<=n;i++){
		int x;
		cin>>x;
		mp[x].push_back(i);
	}
	LL ans=1,t=0;
	for (auto &it:mp){
		auto v=it.second;
		t=1+v.size();
		for (int i=0;i<v.size();i++)
			t=(t+(upper_bound(v.begin(),v.end(),v[i]+d)-(v.begin()+i)-1))%mod;
		ans=(ans*t)%mod;
	}
	cout<<ans-1;
}

组合数学,每个国家都是互相独立的

那么用 unordered_map 存每个国家的位置,mp[x].push_back(i);

遍历每个国家的位置,从位置 \(i\) 向后找第一个大于 \(v_i+d\) 的位置 \(j\)那么 \(j-i-1\) 就是和 \(v_i\) 相距不超过 \(d\) 的座位数

因为选取距离小于 \(d\) 的两个座位是一个组合,那么只用考虑右侧的情况即可

所以对当前国家可以选取的方案数为 \(\sum(j-i-1)+n+1\) 即所有选两个的方案数加上只选一个和一个都不选的方案数

因为每个国家都是独立,考虑乘法原理对于所有的国家数 \(x\) ,方案数为 \(\prod_{i=1}^x ans_i\)

又因为题目要求不能一个都不选,所以最后需要减去 \(1\)

F:

题目大意:

void solve(){
	int n,t;
	cin>>n>>t;
	vector<int> a(n+1),b(n+1);
	for (int i=1;i<=n;i++) cin>>a[i];
	for (int i=1;i<=n;i++) cin>>b[i];
	b[n]=0;
	int l=0,r=3600+1;
	auto judge=[&](int x){
		int dly=0,cnt=0,tim=x;
		for (int i=1;i<=n;i++){
			tim-=dly;
			while (tim<a[i]){
				tim+=x;
				cnt++;
			}
			if (tim>x) tim=x;
			tim-=a[i];
			dly=b[i];
		}
		return cnt>=t;
	};
	while (l+1!=r){
		int mid=l+r>>1;
		if (judge(mid))
			l=mid;
		else
			r=mid;
	}
	if (r==3601) cout<<-1;
	else cout<<r;
}

二分+模拟

坑点在于 \(b_i\) 可能会持续多天,并且最后一天的 \(b_i\) 如果需要推迟到第二天那么也不用算

所以可以提前将 b[n]=0judge 函数写法有点牢

auto judge=[&](int x){
	int dly=0,cnt=0,tim=x;
	for (int i=1;i<=n;i++){
		if (x<a[i]) return true;//如果当前的工作时间不能完成一天的任务,直接返回
		tim-=dly;//减去当前需要的b_i,欠工作时间为负数
		while (tim<a[i]){//剩余的工作时间不能完成,那么就推迟下一天
			tim+=x;//补时间
			cnt++;//天数+1
		}
		if (tim>x) tim=x;//最大时间不能超过x
		tim-=a[i];
		dly=b[i];
	}
	return cnt>=t;
};
posted @ 2025-04-06 22:09  才瓯  阅读(8)  评论(0)    收藏  举报