牛客算法周周练10 解题报告
A 论如何出一道水题(思维)
链接:https://ac.nowcoder.com/acm/contest/5986/A
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
给定 n,求一对整数 (i,j),在满足 1 ≤ i ≤ j ≤ n 且gcd(i,j)=1 的前提下,要求最大化 i+j 的值,n<=1e18
输入描述:
第一行一个整数 n
输出描述:
一行一个整数表示答案
示例1
输入
2
输出
3
题目大意:
给出一个n,找出1-n中的i,j,使得gcd(i,j)==1的情况下i+j最大。
解题思路:
刚开始没有反应过来,一直用的1+n wa了好几发,其实这个题是n和n-1互质,特判一下1的时候ans=2就可以了。AC代码:
#include <iostream>
#include <algorithm>
using namespace std;
typedef unsigned long long ll;
int main()
{
ll n;
cin>>n;
cout<<(n==1?2:(n+n-ll(1)))<<endl;
//system("pause");
return 0;
}
B 暴力出奇迹(二进制贪心)
备注,范围1e5。
题目大意:
给出一串长度为n的序列,找一段连续的子序列,使得sum(i,j)* and(i,j)最大化。
解题思路:
枚举贪心找最大值的序列,因为涉及到按位与,只要这一位出现0,那么这个结果就会都变成0,于是我们从下一位开始枚举,范围1e5,大约是2^20,枚举a[i]&1的情况就可以了,用ans维护一个最大值,最后输出ans。AC代码:
#include <iostream>
#include <algorithm>
#include <algorithm>
using namespace std;
const int _max = 1e5+50;
typedef long long ll;
ll a[_max],n;
int main()
{
cin>>n;
for(int i=0;i<n;i++)
cin>>a[i];
ll ans=-1;
for(int i=0;i<20;i++)//枚举一共20种可能的情况
{
ll t=a[0],sum=0;
for(int j=0;j<n;j++)
{
if(a[j]>>i&1)//只要出现了0,这一位以后的结果就都是0,重新枚举
{
sum+=a[j];
t&=a[j];
ans=max(ans,t*sum);
}
else
{
t=a[j+1];
sum=0;
}
}
}
cout<<ans<<endl;
//system("pause");
return 0;
}
E 跳石头(二分答案)
链接:https://ac.nowcoder.com/acm/contest/5986/E
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
一年一度的“跳石头”比赛又要开始了!
这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。
为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。
输入描述:
输入文件第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。
接下来 N 行,每行一个整数,第 i 行的整数 Di(0 < Di < L)表示第 i块岩石与起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。
输出描述:
输出文件只包含一个整数,即最短跳跃距离的最大值。
示例1
输入
25 5 2
2
11
14
17
21
输出
4
说明
将与起点距离为 2 和14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离17的岩石跳到距离 21的岩石,或者从距离 21 的岩石跳到终点)。数据范围1e18
题目大意:
有一组石头,从起点到终点,计划移走最多m块石头,使得最短跳跃距离最大,输出这个距离。
解题思路:
二分答案,每次去判这个长度是不是够m块石头,如果够的话说明距离大了,把距离调小(r=mid-1),如果不够说明距离小了,调大(l=mid+1),让r始终维持一个不合法的边界,那么最后一次r=mid-1的时候就已经合法了,输出r即可。AC代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=1e5+5;
const ll _max=0x3f3f3f3f3f3f3f3f;
int a[N],n,m,final;
bool countseg(ll mid)
{
ll s=0,ans=0;
for(int i=0;i<=n;i++)
if(a[i]-s<mid)
ans++;
else
s=a[i];
if(ans>m)
return true;
return false;
}
int main()
{
cin>>final>>n>>m;
for(int i=0;i<n;i++)
cin>>a[i];
a[n]=final;
sort(a,a+n+1);
ll l=0,r=final;
while(l<=r)
{
ll mid=(l+r)>>1;
if(countseg(mid))
r=mid-1;//最后一次r一定是合法的
else
l=mid+1;
}
cout<<r<<endl;
//system("pause");
return 0;
}
F 树上求和(dfs+思维)
链接:https://ac.nowcoder.com/acm/contest/5986/F
来源:牛客网
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
有一棵包含n个节点和n-1条边的树,规定树链(u,v)为树上从u到v的简单路径。
树的每条边上都有一个正整数,这个正整数被称作这条边的颜色,规定一条树链的权值w(u,v)为这条树链上所有边的颜色的代数和。
而整棵树的权值为所有不同的树链的权值的代数和。
已知所有边的颜色集合恰好为1到n-1这n-1个不同的正整数,请你为每条边安排一种颜色,使得这棵树的权值尽量小,你不需要给出具体方案,只需要求出这个最小的权值即可。
输入描述:
测试数据第一行,是一个正整数n(1e5),表示树的节点个数
接下来n-1行,每行两个用空格隔开的整数u,v,表示树上有一条边连接u和v
输出描述:
一个整数,表示了这棵树的最小的权值。
示例1
输入
复制
4
1 2
2 3
3 4
输出
复制
19
说明
w(1,2) + w(1,3) + w(1,4) + w(2,3) + w(2,4) + w(3,4)= 3 + 4 + 6 + 1 + 3 + 2 = 19
题目大意:
给出一棵树,构建好之后分配每条边的权值,使得最后树上求和的sum值最小。
解题思路:
dfs+思维题,通过观察图发现,我们要求的最后答案,就是每条边的权值对答案贡献之和,当前这条边经过的次数=左边节点数*右边节点数,我们将每条边的经过次数存一下,从大到小排序,权值从1往上分配,用vector存一下边,用dfs遍历图,dfs(u,v)的意义是:从u到v的节点有几个,递归的去求解即可。AC代码:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int _max = 1e5+50;
vector<ll > vc[_max];
ll len[_max],cnt=0,n;
ll dfs(int u,int v)//dfs遍历图求左右节点数
{
ll sum=1;
for(int i=0;i<vc[u].size();i++)
{
if(vc[u][i]!=v)
sum+=dfs(vc[u][i],u);
}
len[++cnt]=sum*(n-sum);
return sum;
}
int main()
{
cin>>n;
for(int i=1;i<n;i++)
{
int u,v;
cin>>u>>v;
vc[u].push_back(v);
vc[v].push_back(u);
}
dfs(1,0);
sort(len+1,len+1+cnt,greater<ll>());//次数排序
ll ans=0;
for(int i=1;i<=cnt;i++)
ans+=i*len[i];//从1分配权值
cout<<ans<<endl;
//system("pause");
return 0;
}