牛客小白月赛#56
A.阿宁的柠檬
-
题意
- 甜度的分值范围为0a,酸度的分值范围为1b
- 有n个柠檬,求最小分值和最大分值
-
题解
- 最小值为甜度取0,酸度取1,分值为
0*n+1*n
;最大值为甜度,酸度取a,b,分值为a*n+b*n
- 最小值为甜度取0,酸度取1,分值为
-
代码
#include <iostream>
using namespace std;
int main() {
long long a,b,n;//注意爆int
cin>>a>>b>>n;
cout<<n<<' '<<(a+b)*n<<'\n';
return 0;
}
B.阿宁与猫咪
-
题意
- 给定值m;构造一个正整数数列,元素之和为m,且奇数位置之积 与 偶数位之积 的和最小
-
题解
- 猜测结论:构造的数列是m个1
- 证明上述结论成立:全为1时,其奇数位积于偶数位积之和必然为2(m!=1时);其他的构造也就是要替换掉某些1变成不是1的元素,那么必然奇数位或者偶数位积>1,那么之和>2,所以可以证得,上述方案为最优方案
-
代码
#include <iostream>
using namespace std;
int main() {
int m;
cin>>m;
cout<<m<<'\n';
for(int i=1;i<=m;i++) cout<<1<<' ';
return 0;
}
C.阿宁吃粽子
-
题意
-
n条带美味值粽子,总的美味值计算如下,安排n个粽子吃的顺序,使得总美味值最大
W = ∑ k = 1 n a k × 2 k % 10 , k 为吃的次序 W=\sum_{k=1}^{n}a_k\times2^{k\%10},k为吃的次序 W=k=1∑nak×2k%10,k为吃的次序 -
如果有多种符合要求的解,那么把美味值大的放到后面
-
-
题解
- 构造,按要求构造一个最大的排序。先把美味值排序,美味值越大的放在2次幂权值大的位置上,可以用二维vector记录权值k时有哪些位置,然后一次把排好序的粽子排放在位置上
- 对于我来说,难点在代码实现上
-
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N=2e5+10;
int n,a[N],ans[N];//记录美味值,记录答案
vector<int> b[12];//记录权值为0~9的位置有哪些
int main() {
cin>>n;
for(int i=1;i<=n;i++) {
cin>>a[i];
b[i%10].push_back(i);//初始化好对应权值的位置
}
sort(a+1,a+1+n);//排序
for(int i=n;i;i--) {//贪心把美味值大的放在权值大的位置
for(int j=9;j>=0;j--) {
if(b[j].size()) {
int x=b[j].back();//从大的位置开始放,满足题目要求
b[j].pop_back();
ans[x]=a[i];//每填一个就break去填下一个
break;
}
}
}
for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
puts("");
return 0;
}
D.阿宁的质数
-
题意
- 询问q次,长度为n的数组中前x个数中没有出现的最小质数
-
题解
- 由题得,当a为n个从小到大的质数,那么对于任意一个询问,暴力寻找答案时,寻找复杂度最高。因为一旦a有不连续的质数,那么暴力寻找时就不需要从1到x,在x之前就找到没有出现的最小质数了。因此预处理出前2e5个质数就是极限的寻找范围了,同时应该用线性筛1~3e6左右个数才能筛出2e5个素数
- 因为对于所有的位置的答案都是确定的,所以可以先预处理出每个位置的答案。因为查找的范围是一个从1开始的不断向后延展的区间,切寻找的是最小没出现的质数,所以可以利用双指针思维,指向答案的指针随着区间扩展时单调(不严格)递增的。
-
代码
#include <iostream>
using namespace std;
const int N=2e5+10,M=3e6+10;//数组的大小,筛素数的范围
int n,q,a[N],ans[N];//存每个位置的答案
int cnt,primes[M];//筛素数
bool st[M];
bool vis[M];//找答案的辅助数组
void get_primes() {//线性筛素数
for(int i=2;i<M;i++) {
if(!st[i]) primes[cnt++]=i;
for(int j=0;primes[j]<=M/i;j++) {
st[primes[j]*i]=true;
if(i%primes[j]==0) break;
}
}
}
int main() {
get_primes();
//cout<<cnt<<'\n';
cin>>n>>q;
for(int i=1;i<=n;i++) cin>>a[i];
int p=0;//指向答案的指针
for(int i=1;i<=n;i++) {//遍历每个位置找答案
if(a[i]<M) vis[a[i]]=1;//如果这个数在查找范围内,标记已经出现过
while(vis[primes[p]]) p++;//从前往后单调寻找第一个没有出现的质数
ans[i]=primes[p];//答案
}
while(q--) {
int x; cin>>x;
cout<<ans[x]<<'\n';
}
return 0;
}
E.阿宁睡大觉
-
题意
-
给定一个只含有’Z’和’z’的字符串,可以进行删除操作,每次删除一个字符,最多删除k次
-
求下式的最大值
∑ i = 1 l e n ( s ) − 1 w ( s i ) × w ( s i + 1 ) 其中 w ( Z ) = 2 , w ( z ) = 0 \sum_{i=1}^{len(s)-1}w(s_i)\times w(s_{i+1})\\ 其中w(Z)=2,w(z)=0 i=1∑len(s)−1w(si)×w(si+1)其中w(Z)=2,w(z)=0
-
-
题解
- 由于只有’ZZ’的情况才能给答案做贡献,所以可以统计连续Z的子段来统计没有进行删除时的答案
- 再统计所有z的连续子段及其长度(非开头和结尾,因为只要z在开头或者结尾删不删除对答案没有影响),从小到大排序去贪心删除这些子段,每删除一个子段答案多了一个ZZ的答案贡献
-
代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N=2e5+10;
int n,k,ans;
char s[N];
vector<int> sub;//存储z子段的长度
int main() {
cin>>n>>k;
cin>>s+1;
for(int i=1;i<=n;i++) {
int p=i;
while(s[p]==s[i] && p<=n) p++;
p--;
if(s[i]=='Z') ans+=(p-i);//连续的Z子段对答案的贡献
else if(i!=1 && p!=n) sub.push_back(p-i+1);//非两端的z子段记录
i=p;
}
sort(sub.begin(),sub.end());//排序后,贪心删除zz子段
for(auto x:sub)
if(x<=k) {k-=x; ans++;}
cout<<ans*4<<'\n';//每个ZZ的分数为4
return 0;
}
F.阿宁去游玩
-
题意
- 给定一个含n个节点的无向图,每个节点有0,1两种性质,如果性质相同那么边权为x,否则为y
- 可以通过魔法将除了所在点的其他点都变换性质,花费边权z
- 问从1到n的最短路为多少
-
题解
- 正权最短路可以直接用spfa
- 建图,边权有两种情况,不用魔法直接是x(y),使用魔法为y+z(x+z),选其中边权小的即可。注意,相邻的点相对性质不变,所以不需要考虑全局怎么变化的
-
代码
#include <iostream>
#include <queue>
using namespace std;
const int N=2e5+10;
typedef long long LL;
const LL INF=1e18+10;//注意数据范围
int n,m,x,y,z,a[N];//数组为每个节点的性质
int h[N],e[2*N],w[2*N],ne[2*N],idx;//无向图
bool st[N];//最短路
LL dis[N];
void add(int a,int b,int c) {//加一条权值为c的边
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
int main() {
cin.tie(0)->sync_with_stdio(false);
cin>>n>>m>>x>>y>>z;
for(int i=1;i<=n;i++) cin>>a[i],h[i]=-1,dis[i]=INF;//初始化
while(m--) {//建图
int u,v;
cin>>u>>v;
if(a[u]==a[v]) {
add(u,v,min(x,y+z));
add(v,u,min(x,y+z));
}
else {
add(u,v,min(y,x+z));
add(v,u,min(y,x+z));
}
}
//spfa最短路
dis[1]=0;
queue<int> q;
q.push(1); st[1]=1;
while(q.size()) {
int t=q.front();q.pop(); st[t]=0;
for(int i=h[t];~i;i=ne[i]) {
int j=e[i];
if(dis[j]>dis[t]+w[i]) {//注意是dis[t]和w[i]
dis[j]=dis[t]+w[i];
if(!st[j]) q.push(j),st[j]=1;
}
}
}
cout<<dis[n]<<'\n';//输出最短路答案
return 0;
}