牛客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\)。现在也就是考虑是否有解,以及有解情况下如何构造。
- 我们可以构造一个字符串\(p=zzzzzz\),求出其哈希值\(up=hash(p)\),如果\(hash(s)+m> up\),那么肯定无解。
- 如果有解情况,我们考虑将\(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;
}

浙公网安备 33010602011771号