牛客2025秋季算法编程训练联赛5-基础组

D

这道题非常有意思,打表找规律。

我们用\(sg[k][last]\)表示上次选了\(last\)个,现在剩下\(k\)个的状态是否是必败态,我们按照题意,模拟进行打表,可以发现规律是只要\(n=2^k(k\in N)\),那么输出Alice;否则输出Bob

tips:检查是否为\(2^k\),可以用\(n\&(n-1)==0\)

#include <bits/stdc++.h>
using namespace std;
#define inf 1e18
#define endl '\n'
#define int long long
typedef  long long ll;
typedef pair<int, int> pii;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
const int N = 100 + 9, M = 2e5 + 9, mod = 1e9 + 7;
int sg[N][N],win[N];
//sg[m][last]表示还剩余m张,最多可以拿last张
int can_win(int m,int last){
	if(m==0) return 0;
	if(last>=m) return 1;
	if(sg[m][last]!=-1) return sg[m][last];
	
	set<int> s;
	for(int j=1;j<=last;j++){
		s.insert(can_win(m-j,j));
	}
	int mex=0;
	while(s.count(mex)) mex++;
	return sg[m][last]=mex;
}
void bf(){
	int n=100;
	memset(sg,-1,sizeof sg);
	for(int i=1;i<=n;i++){
		if(i==1){
			win[i]=0;
			continue;
		}
		for(int k=1;k<n;k++){
			if(can_win(i-k,k)==0){
				win[k]=1;
				break;
			}
		}
	}
	for(int i=1;i<=n;i++) cout << win[i] << " \n"[i==n];
}

void solve() {
//	int n;
//	cin >> n;
//	if((n&(n-1))==0) cout << "Alice" << endl;
//	else cout << "Bob" << endl;
	bf();
}
/*

*/
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t = 1;
//	cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}

E

要找到和\(s\)哈希碰撞的\(s'\),也就是\(hash(s)\equiv hash(s')\mod m\),且\(s'\)是比\(s\)大且字典序最小的字符串。

sol

要满足上式,我们令\(hash(s')=hash(s)+k\times m\),因为要最小,这里\(k=1\)。现在也就是考虑是否有解,以及有解情况下如何构造。

  1. 我们可以构造一个字符串\(p=zzzzzz\),求出其哈希值\(up=hash(p)\),如果\(hash(s)+m> up\),那么肯定无解。
  2. 如果有解情况,我们考虑将\(hash(s)+m\)转换为\(26\)进制数\(num\),如果位数不够\(6\),那么用0补齐,然后把\(num\)加在\(aaaaaa\)上得到\(s'\)
#include <bits/stdc++.h>
using namespace std;
#define inf 1e18
#define endl '\n'
#define int long long
typedef  long long ll;
typedef pair<int, int> pii;
int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
const int N = 2e5 + 9, M = 2e5 + 9, mod = 1e9 + 7;
string s;
int m;
void solve() {
	int sum=0;
	for(int i=0;i<=5;i++){
		sum=sum*26+(s[i]-'a');
	}
	int t=sum+m;
	string up="zzzzzz";
	int lim=0;
	for(int i=0;i<=5;i++) lim=lim*26+(up[i]-'a');
	if(t>lim){
		cout << -1 << endl;
		return;
	}
	string s1="aaaaaa";
	vector<int> base;
	while(t){
		base.push_back(t%26);
		t/=26;
	}
	while(base.size()<6) base.push_back(0);
	reverse(base.begin(),base.end());
	int i=0;
	for(int v:base){
		s1[i]+=v;
		i++;
	}
	cout << s1 << endl;
}
/*

*/
signed main() {
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	while(cin >> s >> m){
		solve();
	}
	return 0;
}

F

给定\(n\)个点\((x_i,y_i)\),现在可以在\(x\)轴上选择一个点\(A(a,0)\),使得\(\max_{i=1}^{n}{dist_{A,point_i}}\)最小。

经典\(trick\),最小化最大值,我们可以使用二分来解决这道题,但是这里的\(check\)非常不一样,我们是二分这个最大距离\(D\),然后对于每一个点\(i\),我们可以根据等式\((x-x_i)^2+y_i^2\leq D\)求出\(x\)可行的范围\([L_i,R_i]\),之后对所有合法区间求交集,得到最终的\([L,R]\),如果交集为空,说明当前\(D\)小了,那么我们扩大\(D\),扩大范围。

对于一次\(check\),我们按照下面步骤:

  • 检查\(abs(a[i].y)\leq mid\),如果不满足,那么肯定与\(x\)轴没有交点,直接返回0;
  • 求区间交集,\(\delta=\sqrt{mid\times mid-a[i].y\times a[i].y}\)\(L_i=a[i].x-\delta,R_i=a[i].x+\delta\),我们对\(l:=\max(l,L_i),r:=\min(r,R_i)\)
  • 最后返回\(return \space l\leq r\)

为了提高精度,我们直接循环100次\(check\)

struct node{
	double x,y;
};
void solve() {
	int n;
	cin >> n;
	vector<node> a(n+1);
	double lo=0.0,hi=0.0;
	for(int i=1;i<=n;i++){
		cin >> a[i].x >> a[i].y;
		hi=max(hi,sqrt(a[i].x*a[i].x+a[i].y*a[i].y));
	}
	auto check=[&](double mid)->bool{
		for(int i=1;i<=n;i++){
			if(abs(a[i].y)>mid) return 0;
		}
		double l=-inf,r=inf;
		for(int i=1;i<=n;i++){
			double delta=sqrt(mid*mid-a[i].y*a[i].y);
			l=max(l,a[i].x-delta);
			r=min(r,a[i].x+delta);
		}
		return l<=r;
	};
	for(int i=1;i<=100;i++){
		double mid=(lo+hi)/2;
		if(check(mid)){
			hi=mid;
		}else{
			lo=mid;
		}
	}
	cout << fixed << setprecision(6) << lo << endl;
}
posted @ 2025-11-13 17:22  alij  阅读(12)  评论(0)    收藏  举报