【题目链接】:
牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)https://ac.nowcoder.com/acm/contest/55629
【通过情况】:ADGIJL
A | An Easy Problem |
【题意】
对于一个点 A(x,y) 在平面上,我们定义 F(x,y) 如∣x∣∗∣y∣ ,表示矩阵和顶点的面积(x,0),(0,y),(0,0),(x,y)。提供了一个坐标点(x,y),需要找到这个区间内第 k 大的面积。
【思路】
这道题目有一个比较大的问题就是只能大概知道大的数分布趋势,但是很难找到规律一个一个数,这里转换了一下思路,转化为寻找第 m*n-k+1 大的数字,用二分开定位面积的大小,并且验证这个对应的面积是第几大的面积,验证的时候,我们可以通过除法去找有多少个比二分的面积要小的数,因为第一行就是1的倍数,第二行就是2的倍数,第n行就是n的倍数,可以通过除法快速求得。这里也要注意一下不能超过m,需要取最小值。
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n,m,k;
bool check(LL a)
{
LL res=0;
for(LL i=1;i<=n;++i)
{
res+=min(m,a/i);
}
if(res>=k)return true;
return false;
}
int main()
{
cin>>n>>m>>k;
k=n*m-k+1;
LL left=1,right=n*m;
while(left<right)
{
LL mid=left+(right-left)/2;
if(check(mid))right=mid;
else left=mid+1;
}
cout<<left;
return 0;
}
D | Double |
【题意】
给你一个数列 n 个数字,每一个数字都可以和相邻的数字对抗,如果不小于相邻的数字就能消灭对方并把自己翻倍,经过 n-1 个回合,请输出有机会留到最后的数字的下标。
【思路】
输入的时候找到最大的数字,如果后面有一个数字翻倍的过程中大于最大值,说明一定能留到最后,然后对每一位数字进行深搜翻倍计算,如果翻倍的过程中遇到两边都大于自己,说明不能留到最后。
【代码】
#include <bits/stdc++.h>
using namespace std;
long long a[500005],maxt,ans,atk;
bool b[500005];
int n;
bool dfs(int l,int r)
{
if(atk>=maxt)
{
return 1;
}
if(atk>=a[l]&&l!=0)
{
atk*=2;
return dfs(l-1,r);
}
if(atk>=a[r]&&r<=n)
{
atk*=2;
return dfs(l,r+1);
}
return 0;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
maxt=max(maxt,a[i]);
}
for(int i=1;i<=n;i++)
{
atk=a[i];
if(dfs(i-1,i+1))
{
b[i]=1;
ans++;
}
}
cout<<ans<<endl;
for(int i=1;i<=n;i++)
{
if(b[i])
{
printf("%d ",i);
}
}
return 0;
}
G | Good Game, GG |
【题意】
两个人玩游戏,A每次可以选择一个奇数 a ,分成两个和为 a 的正整数,B每次可以选择一个偶数 b ,分成两个和为 b 的正整数,由 A 先开始,谁先无法操作就输了,给你一串数字,请判断最后的赢家。
【思路】
对于 A 和 B 来说,每个人都希望自己的操作次数更多,对方的操作次数更少,所以 B 不会将偶数分解成两个奇数,只会分解成两个偶数,而且 B 最后不会将 2 分解为两个 1 ,因为自己的这一步操作会使得 A 多两步操作次数。对于 A 来说,A 把奇数 n 分解为 一个 2 还有 n-2的时候是最优的,因为分解为更大的偶数的话 B 的操作次数就增加了,如果 B 对 2 进行操作的话 A 只会增加的更多。不到最后 B 肯定不会对 2 进行操作的,换句话说,如果 B 对2进行操作的话,B 就输了。综上所述,A 的操作次数就是所有奇数能减去多少次2,还有能删除多少次1,B 的操作次数就是看偶数能减去多少次2达到2。
【代码】
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
long long a=0,b=0;
for(int i=0;i<n;i++)
{
int temp;
cin>>temp;
if(temp%2)
{
a+=temp/2+1;
}
else if(temp!=2)
{
b+=temp/2-1;
}
}
if(a>b)
{
cout<<"Alice"<<endl;
}
else
{
cout<<"Bob"<<endl;
}
}
return 0;
}
I | Industrial Nuclear Water |
【题意】
在一个直角坐标系空间中,有三个障碍物,分别使用了三个函数表示,现在给你两个点的坐标,询问是否被障碍物隔开
【思路】
这道题目其实不难,但是判断的方式需要注意一下,对于一个障碍物(一个函数)来说,很容易判断,只需要判断是不是在同一侧就好了,对于多个障碍物也是如此,如果没有被障碍物隔开,那么就对于每一个函数来说这两个点都应该在同一侧,所以对两个点都进行每一个函数的判断就好了。需要注意的是,虽然只是三个函数判断,但是都带有绝对值,画出来的图像也有重合的部分,建议首要判断“No”的情况,最后剩下来的就是“Yes”的情况。
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
bool check(LL a,LL b,LL c)
{
return 1000*a>b*b+c*c;
}
int main()
{
int t;
cin>>t;
while(t--)
{
LL x1,y1,z1,x2,y2,z2;
cin>>x1>>y1>>z1>>x2>>y2>>z2;
if(check(x1,y1,z1)!=check(x2,y2,z2))cout<<"No\n";
else if(check(y1,x1,z1)!=check(y2,x2,z2))cout<<"No\n";
else if(check(z1,x1,y1)!=check(z2,x2,y2))cout<<"No\n";
else if(check(-x1,y1,z1)!=check(-x2,y2,z2))cout<<"No\n";
else if(check(-y1,x1,z1)!=check(-y2,x2,z2))cout<<"No\n";
else if(check(-z1,x1,y1)!=check(-z2,x2,y2))cout<<"No\n";
else cout<<"Yes\n";
}
return 0;
}
J | Jerry |
【题意】
多个询问,每次回答一个数是由多少个平方数加减得到。
【思路】
这道题目是比较诡异的,一开始我是想排除平方数,因为结果固定为1,然后我证明了奇数可以两步做出来(平方差公式,a^2-b^b=(a+b)*(a-b), 如果 a-b=1 那么平方的差就是等于 a+b=2*b+1 设计一个奇数)但是后面发现最大不能超过 1e5 ,然后这个方法的适用性就只有 316 以内的奇数,在后面我们就摆烂了盲猜一波最多就是 3 ,然后就直接暴力。但是还没有证明的方法,后面更新。
【代码】
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5;
int main() {
set<int> se, p;
for (int i = 1; i <= 316; i++) {
for (int j = i; j <= 316; j++) {
se.insert(i * i + j * j);
se.insert(j * j - i * i);
p.insert(i * i);
}
}
int q, d;
cin >> q;
while (q--) {
cin >> d;
if (p.find(d) != p.end()) {
cout << 1 << endl;
}else if (se.find(d) != se.end()) {
cout << 2 << endl;
}else {
cout << 3 << endl;
}
}
return 0;
}
L | League of Legends |
【题意】
三个队伍轮着打比赛,一开始概率相同,后面的比赛获胜的一队和没有打的一队继续打,谁先输两局就结束比赛,问比赛的期望轮数。
【思路】
这三个队伍一开始谁先打都是一样的,后面直接枚举就好了,见下面的图片,蓝色圈圈表示一开始 A 和 B 先开始,黑色线尖指着的表示谁输了,后面表示对弈的队伍,蓝色字表示输的情况,红色表示最后的结果。