CF951

A

题意

给你一个数列,对于其中任何子数列中的最大数,你需要找出一个\(k\),保证\(k\)都严格小于他们。

方法

不难想到,都是由二位串构成,计算每一个二位串的最大数,然后选择最小的,减去1即可。

代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve()
{
int n; cin>>n;
std::vector<ll> a(n,0);
for(int i=0;i<n;i++)
{
cin>>a[i];
}
std::vector<ll> b(n,0);
for(int i=1;i<n;i++)
{
b[i]=max(a[i-1],a[i]);
}
ll minn=1e9;
for(int i=1;i<n;i++)
{
minn=min(minn,b[i]);
}
cout<<minn-1<<"\n";
}
int main()
{
int t=1;
cin>>t;
while(t--) solve();
return 0;
}

B

题意

给你一个\(x,y\),要求你对\(a,b\)进行以下操作:$a_i=i x, b_j=j y \(,你需要找出最长的连续一段,使得以上条件一直成立,输出长度。\)a,b$都是从1开始的自然数序列。

方法

不难得到:\(i \oplus x = j \oplus y\),利用异或运算的性质,我们可以得到\(i \oplus j= x \oplus y\)\(x \oplus y\)

是已知的,我们需要的是\((i+1) \oplus (j+1)= x \oplus y\)也成立,尽可能延申下去。

可以发现,每一次加一,都是对应位改变:

0001000
1011000

\(0 \oplus 0=0,1 \oplus 1=0\),这样\(i\oplus j\)不变,如果发生了进位,那么会影响到后面的改变,\(i \oplus j\)也就不等于\(x \oplus y\),我们只需要从头开始找最低的1位即可。

这里可以使用\(lowbit(x)\)进行计算。

代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve()
{
ll x,y; cin>>x>>y;
x^=y;
cout<<(x&(-x))<<"\n";
}
int main()
{
int t=1;
cin>>t;
while(t--) solve();
return 0;
}

C

题意

给你若干个\(k_i\),要求你每次对\(k_i\)进行下注\(num\),保证收获\(k_i *num\)大于全部\(num\)的总和。\(num\)就是你针对每一个\(k_i\)的赌注。

方法

不难想到,可以利用他们的最小公倍数进行分配,然后计算如何成立即可。

代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve()
{
int n; cin>>n;
std::vector<ll> v(n,0);
for(int i=0;i<n;i++)
{
cin>>v[i];
}
ll sum=1;
for(int i=0;i<n;i++)
{
sum=sum*v[i]/gcd(sum,v[i]);
}
ll ans=0;
std::vector<ll> a(n,0);
for(int i=0;i<n;i++)
{
a[i]=sum/v[i];
ans+=a[i];
}
if(ans<sum)
{
for(int i=0;i<n;i++)
{
cout<<a[i]<<" \n"[i==n-1];
}
}
else cout<<-1<<"\n";
}
int main()
{
int t=1;
cin>>t;
while(t--) solve();
return 0;
}

D

题意

给你一个二进制串,你可以选择一个位置\(p\),将\(s[1······p]\)进行反转,然后\(s[p+1······n]\)接到\(s[1······p]\)前面,要求你将其进行操作后,满足\(k\)个0或者1循环出现。如果可以,输出位置,否则输出\(-1\)

方法

不难想到,我们直接找第一个不符合的情况,然后确定反转的位置进行操作得到对应的字符串,然后再进行检验:判断是否符合要求。

代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve()
{
int n,k; cin>>n>>k;
string s; cin>>s;
int pos=0;
int cur=1;
int i=0;
for(i;i<n;)
{
cur=1;
char ch=s[i++];
while(i<n && s[i]==ch)
{
cur++;
i++;
}
if(cur==k) continue;
else if(cur<k)
{
pos=i;
break;
}
else
{
pos=i-k;
break;
}
}
if(pos==0)
{
pos=n;
}
string tmp=s.substr(0,pos);
reverse(tmp.begin(),tmp.end());
string t=s.substr(pos)+tmp;
bool ok=true;
for(int i=1;i<k;i++) //先检验第一个周期内的是否符合要求
{
if(t[i]!=t[0])
{
ok=false;
break;
}
}
for(int i=0;i+k<n;i++) //检验所有周期内的是否符合要求
{
if(t[i]==t[i+k])
{
ok=false;
break;
}
}
if(ok)
{
cout<<pos<<"\n";
}
else cout<<-1<<"\n";
}
int main()
{
int t=1;
cin>>t;
while(t--) solve();
return 0;
}

E

题意

给你若干个点,你需要找出是否存在三个点,彼此之间的距离都是\(k\),这里的距离是曼哈顿距离。

方法

由于距离是确定的,范围又很大,我们采取二分。

研究样例可以发现:符合情况的:必定存在两点:\((x,y),(x+\frac{d}{2},y-\frac{d}{2})\),可能有相关的变形,后面处理即可。

可以进行如下操作,我们先查询每一点距离最近的,\(s\)距离不变且\(x\)距离等于增加了\(\frac{k}{2}\)的点,然后将其加入到新的数组中,再从这个数组中进行二分,查找\(s\)距离增加了\(\frac{k}{2}\)\(x\)距离小于\(x+\frac{d}{2}\)的点,如果存在,则是一组合法的解,否则交换坐标进行计算。

代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
void solve()
{
int n; cin>>n;
int d; cin>>d;
std::vector<int> x(n),y(n);
for(int i=0;i<n;i++) cin>>x[i]>>y[i];

int A=0,B=0,C=0;
auto work=[&]()
{
std::vector<std::array<int,3>> a(n);
for(int i=0;i<n;i++)
{
a[i]={x[i]+y[i],x[i],i};
}
sort(a.begin(),a.end());

std::vector<std::array<int,4>> v;
for(auto [s,x,i]: a)
{
auto it=lower_bound(a.begin(),a.end(),std::array{s,x+d/2,0});
if(it!=a.end() && (*it)[0]==s && (*it)[1]==x+d/2)
{
v.push_back({s,x,i,(*it)[2]});
}
}

for(auto [s,x,i]: a)
{
auto it=lower_bound(v.begin(),v.end(),std::array{s+d,x,0,0});
if(it!=v.end() && (*it)[0]==s+d && (*it)[1]<=x+d/2)
{
A=i+1;
B=(*it)[2]+1;
C=(*it)[3]+1;
return;
}
}
};

for(int i=0;i<4;i++)
{
work();
if(A>0) break;
for(int j=0;j<n;j++)
{
swap(x[j],y[j]);
x[j]*=-1;
}
}
cout<<A<<" "<<B<<" "<<C<<"\n";
}
int main()
{
int t=1;
cin>>t;
while(t--) solve();
return 0;
}