牛客小白月赛74 题解

当时过了点了就没打,现在补上


A. 简单的整除

题面

AC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int d[4]={2,3,5,7};
int main() {
ios::sync_with_stdio(false);
int x;
cin>>x;
bool flag=false;
for(int i=0;i<4;i++)
{
if(x%d[i]==0)
{
flag=true;
break;
}
}
if(flag)
cout<<"YES"<<'\n';
else
cout<<"NO"<<'\n';
return 0;
}


B. 整数划分

题面

肯定是按照1,2,3,...这样下去是最优的,只用关注最后一个的取值就行

AC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
vector<int>s;
int main() {
ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--)
{
s.clear();
int n;
cin>>n;
int i=1;
for(i=1;i<=n;i++)
{
s.push_back(i);
n-=i;
}
if(i>n)
s.push_back(n);
int l=s.size();
if(s[l-1]<=s[l-2])
{
s[l-2]+=s[l-1];
s.pop_back();
}
for(int i=0;i<s.size()-1;i++)
cout<<s[i]<<' ';
cout<<s[s.size()-1]<<'\n';
}
return 0;
}


C. 传送阵

题面

显然就是统计不同值的格子数

AC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
map<int,bool>mp;
int main() {
ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--)
{
mp.clear();
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
int x;
cin>>x;
mp[x]=true;
}
}
cout<<mp.size()<<'\n';
}
return 0;
}


D. 修改后的和

题面

每一次操作之后对总和的贡献为 A[k] ⋅ (nk+1)

如果选择要对 A[i1], A[i2], ..., A[im] 进行操作,那么从右往左依次操作是最优的,因为先操作左边的会使右边的数变小

综上,只用找出来 A[k] ⋅ (nk+1) 最大的几个 k 就行

AC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
ll a[N];
int main() {
ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--)
{
int n,m;
cin>>n>>m;
ll sum=0;
vector<ll>s;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(a[i]>0)
s.push_back(a[i]*(n-i+1));
sum+=a[i];
}
if(s.size()==0)
{
cout<<sum<<'\n';
continue;
}
sort(s.begin(),s.end());
reverse(s.begin(),s.end());
for(int i=0;i<min(m,(int)s.size());i++)
sum-=s[i];
cout<<sum<<'\n';
}
return 0;
}


E. 幼稚园的树2

题面

可以发现每棵树的生长是有循环的,计算一下就行

AC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+5;
int h[N],a[N];
int main() {
ios::sync_with_stdio(false);
int T;
cin>>T;
while(T--)
{
int n,m,k,b;
cin>>n>>m>>k>>b;
for(int i=1;i<=n;i++)
cin>>h[i];
for(int i=1;i<=n;i++)
cin>>a[i];
if(m==1)
{
for(int i=1;i<=n;i++)
cout<<h[i]<<' ';
cout<<'\n';
continue;
}
for(int i=1;i<=n;i++)
{
int st=(k-h[i])/a[i]+1;
int s=(k-b)/a[i]+1;
if(st>m-1)
cout<<h[i]+(m-1)*a[i]<<' ';
else
cout<<b+(m-1-st)%s*a[i]<<' ';
}
cout<<'\n';
}
return 0;
}


F. 最便宜的构建

题面

查询是否连通,可以使用并查集

正着思考不太容易,可以考虑枚举验证,发现枚举验证挺容易,但是复杂度很大。用二分优化

AC代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m,k;
const int N=1e5+5;
struct arc
{
int a,b,w;
}a[N];
int f[N];
vector<int>q[N];
bool cmp(arc x,arc y){return x.w<y.w;}
int find(int x)
{
return x==f[x]?x:f[x]=find(f[x]);
}
void merge(int x,int y)
{
x=find(x);y=find(y);
if(x!=y)
f[x]=y;
}
bool judge(int x)
{
for(int i=1;i<=n;i++)
f[i]=i;
for(int i=1;i<=x;i++)
merge(a[i].a,a[i].b);
for(int i=1;i<=k;i++)
{
int now=find(q[i][0]);
for(auto x:q[i])
if(find(x)!=now)
return false;
}
return true;
}
int main() {
ios::sync_with_stdio(false);
cin>>n>>m;
for(int i=1;i<=m;i++)
cin>>a[i].a>>a[i].b>>a[i].w;
cin>>k;
for(int i=1;i<=k;i++)
{
int x;
cin>>x;
for(int j=1;j<=x;j++)
{
int y;
cin>>y;
q[i].push_back(y);
}
}
sort(a+1,a+1+m,cmp);
int l=1,r=m,ans=m;
while(l<r)
{
int mid=(l+r)>>1;
if(judge(mid))
{
ans=mid;
r=mid;
}
else
l=mid+1;
}
cout<<a[ans].w<<'\n';
return 0;
}

并查集一定要加查找优化,不加会炸,一下午的debug的教训🤡


img_show