第八届“图灵杯”NEUQ-ACM程序设计竞赛个人赛
A 切蛋糕
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
Special Judge, 64bit IO Format: %lld
题目描述
龙龙有一块蛋糕,现在他想将蛋糕平均切成\(k\)块,分给他的\(k\)名hxd(好兄弟)。但是不幸的是,因为龙龙不擅长切蛋糕,他每一次只能将一块蛋糕平均分成两份。例如,将一块大小为\(1\)的蛋糕分割成两块大小为\(1/2\)的蛋糕,将一块大小为\(1/2\)的蛋糕分割成两块大小为\(1/4\)的蛋糕,以此类推。由于龙龙手法有限,蛋糕的大小最小不能小于\(1/2^{15}\)。
除此之外,为了更有仪式感,龙龙在将切好的蛋糕分给自己的hxd之前,要先将蛋糕打包。龙龙可以将切好的任意数量块蛋糕打包在一起,并交给他的一位hxd。需要注意的是,蛋糕在打包好后就不能再被切分了。为了公平起见,龙龙希望他的每一位hxd分得的蛋糕大小是相等的,即每位hxd分得的蛋糕的大小,与\(1/k\)的差距的绝对值不能大于\(1/2^{10}\)。因为龙龙很懒,所以他希望蛋糕的总操作次数(每次打包或切分算一次操作)不超过\(6000\)。
现在龙龙想请你为他设计一种方案,使得他能够在\(6000\)次操作内,将蛋糕公平地分给他的hxd们。
输入描述
输入在一行中给出\(1\)个不超过\(2^{10}\)的正整数\(k\)。
输出描述
在第一行输出一个不超过\(6000\)的正整数\(N\),表示方案的操作次数。
接下来的\(N\)行描述该方案的\(N\)次操作。
对于第\(i\)次操作,若该操作要进行切分操作,则在该行行首输出1
,之后输出要切分的蛋糕的大小。例如:若要将一块大小为\(1/{2^1}\)的蛋糕分割成两块大小为\(1/{2^2}\)的蛋糕,则在该行输出1 1
;若要将一块大小为\(1/{2^2}\)的蛋糕分割成两块大小为\(1/{2^3}\)的蛋糕,则在该行输出1 2
。初始时完整的蛋糕,大小视为\(1/{2^0}\)。
若该操作要进行打包操作,则在该行行首输出2
,之后输出一个正整数\(m\),代表要打包的蛋糕块数。之后输出\(m\)个正整数,分别代表要打包的\(m\)快蛋糕的大小。例如:若要将一块大小为\(1/{2^1}\)的蛋糕和一块大小为\(1/{2^2}\)的蛋糕进行打包,则在该行输出2 2 1 2
;若要将两块大小为\(1/{2^2}\)的蛋糕进行打包,则在该行输出2 2 2 2
。
需要注意,一块蛋糕只有在被打包后才可以被龙龙送给他的hxd。所以你至少应进行\(k\)次打包操作。每份被包装的蛋糕的大小与\(1/k\)的差距的绝对值不应大于\(1/{2^{10}}\)。
示例1
输入
2
输出
3 1 0 2 1 1 2 1 1
题解
1.首先可以对\(1/k\)进行二进制拆分,得到其小数点后\(14\)位的二进制值。
2.显而易见,若要将蛋糕平均分成\(k\)份,则其二进制下每一个\(1\)处,都应有\(k\)份与之对应的蛋糕。
例如,对于\(k=5\),\(1/k\)的二进制为\(0.001100110011\),则应存在\(5\)份大小为\(0.001\)的蛋糕,\(5\)份大小为\(0.0001\)的蛋糕,以此类推。
3.合并时,每次将其二进制下\(1\)的格数份蛋糕合并,即可得到规定大小的值。
4.所以在拆分时,只需将\(1/k\)在二进制下每一位为\(1\)的大小的蛋糕保存\(k\)份,其余蛋糕全部向下拆分,最后再花费\(k\)步将蛋糕合并即可。
由于误差不应大于\(2^{-10}\),所以最坏情况下只需花费\(2^{-10}-1=1023\)次即可完成拆分,则最坏情况下总操作次数为\(1023+k\leqslant 2047\),小于\(6000\),满足要求。
ps.1其实对于本题,由于放宽了数据要求,存在一个简单的做法,只需暴力花费\(2047\)次将所有蛋糕大小都拆分成\(1/2048\),再通过计算得到\(x\),使得\(abs(\frac{x}{2048}-\frac{1}{k}) \leqslant 2^{-10}\)。
ps.2再花\(k\)步,每步将\(x\)个蛋糕合并,总操作次数为\(2047+k \leqslant 3071\),也可满足要求。
- TAG:二进制;思维
std.cpp
#include<bits/stdc++.h>
using namespace std;
const double eps=1e-10;
bool v[20];
/*
v[i]代表若将1/k拆分后,从前往后第i位为1,其值即为真;
第一位默认为1/{2^0}即个数位;
例如k=5:
v数组的值为 0 00110011001100……
二进制下1/k为 0.00110011001100……
*/
int num[20];
/*
num[i]代表 在拆分时,大小为1/{2^i}的蛋糕有多少块;
*/
vector<int>op;
//op为拆分蛋糕时,需要储存的答案
int main(){
int k;
cin>>k;
double tmp=1.0/k;
double sum=0.0,cnt=1.0;
/*
tmp即为1/k,接下来将进行二进制拆分;
sum代表了当前拼凑出来的数值;
cnt代表了1/{2^{cnt}};
*/
for(int i=0;i<15;++i){
if(sum+cnt-tmp<=eps){
/*
每当尝试拼凑时,sum+cnt即为当前拼凑出来的数;
例如k=5,当sum=0、cnt=1/{2^3}时,sum+cnt-tmp<0;
说明在二进制下的k中的第三位是1,这样减去sum+cnt,k后面的位数才有1的剩余,使得sum+cnt-tmp<0;
这里由于精度问题,将0设置成为一个较小的精确值,为1^{10};
这里鄙人也不是十分理解,鄙人拙见,望互相讨论;
*/
sum+=cnt;
v[i]=1;
}
cnt/=2;
}
num[0]=1;
for(int i=0;i<15;++i){
int ret=0;
if(v[i])
ret=k; //当某一位上为1时,留出k份,其余全部拆分
while(num[i]>ret){
op.push_back(i);
num[i]--;
num[i+1]+=2;
}
}
vector<int>ans;
//ans为合并蛋糕时,需要储存的答案
for(int i=0;i<=15;++i){
if(v[i]) ans.push_back(i);
}
//如果1/k的某一位上有1,则这k个合并出来的蛋糕就需要1份这样大小的蛋糕
cout<<op.size()+k<<endl;
//先输出操作次数,即为拆分次数+合并次数
for(auto t:op){
cout<<1<<" "<<t<<endl;
}
//输出拆分操作答案
for(int i=1;i<=k;++i){
cout<<2<<" "<<ans.size();
for(auto t:ans){
cout<<" "<<t;
}
cout<<endl;
}
//输出k个蛋糕的合并方案的答案
return 0;
}
B 小宝的幸运数组
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 65536K,其他语言131072K
64bit IO Format: %lld
题目描述
对于小宝来说,如果一个数组的总和能够整除他的幸运数字\(k\),就是他的幸运数组,而其他数组小宝都很讨厌。现在有一个长度为\(n\)的数组,小宝想知道这个数组的子数组中,最长的幸运子数组有多长。
对于子数组的定义,如果可以通过从开头和从结束分别删除若干个(可以为零或全部,前后删除个数不必相同)元素来从数组\(b\)获得数组\(a\),则称数组\(a\)是数组\(b\)的子数组。(子数组包含原数组,但不包含空串)
输入描述
多组输入。第一行包含一个整数\(T(1 \leqslant T \leqslant 10)\),表示有\(T\)组测试数据。每组测试数据包含两行,第一行包含两个整数\(n\)和\(k(1\leqslant n \leqslant 10^5,1 \leqslant k \leqslant 10^5)\),分别表示数组长度和小宝的幸运数字。第二行包含\(n\)个空格分隔的整数\(a1,a2,.,an(0 \leqslant ai \leqslant 10^9)\),为数组的元素。
输出描述
对于每组数据,输出和能被k整除的最长子数组的长度。如果没有这样的子数组,则输出−1
。
示例1
输入
4 3 3 1 2 3 3 5 1 2 3 3 7 1 2 3 1 6 5
输出
3 2 -1 -1
题解
1.用前缀和a[]
预处理数组,则从第\(i\)位到第\(j\)位的子串和为a[j]-a[i-1]
;
2.又\((a-b)\%k=a\%k-b\%k\),所以a[j]-a[i-1]
整除\(k\)等价于a[j]
和a[i-1]
对\(k\)取模的结果相等;
3.然后只需要贪心地寻找距离最远的相同取模结果即可。
- TAG:思维
std.cpp
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100005
#define inf 0x3f3f3f3f
#define intq() int T;cin>>T;while(T--)
ll a[N];
int main(){
intq(){
int t[N]={0},xm[N],ym[N]={0};
for(int i=0;i<N;++i) xm[i]=inf;
int n,p; cin>>n>>p; //此代码中k为p
bool A=1;
t[0]=1; xm[0]=ym[0]=0;
//xm[i]表示 a[i]%k 能取到的最小的坐标
//ym[i]表示 a[i]%k 能取到的最大的坐标
//t[i] 表示 a[i]%k 出现的次数
//A 为是否有解的标志
for(int i=1;i<=n;++i){
ll x; cin>>x;
a[i]=(a[i-1]+x)%p;
xm[a[i]]=min(xm[a[i]],i);
ym[a[i]]=max(ym[a[i]],i);
t[a[i]]++;
if(t[a[i]]>=2) A=0;
//如果某个a[i]%k出现次数大于等于2次,必然存在一个答案
//使得a[j]%k=a[i]%k,即a[j]和a[i-1]对k取模的结果相等,即a[j]-a[i-1]整除k
}
//如果整个数组读下来,没有出现次数大于等于2次的a[i]%k,说明没有答案
if(A) cout<<"-1\n";
else{
int ans=0;
//否则,在0~k之间寻找ym[i]-xm[i]的最大值,即为答案
for(int i=0;i<N;++i){
if(t[i]>=2){
ans=max(ans,ym[i]-xm[i]);
}
}
cout<<ans<<"\n";
}
}
return 0;
}
PZ's Solution
1.思路与题解本质相同,但不需要许多麻烦的记录;
2.对于样例
3 3
1 2 3
将a[i]%k
处理后,可以发现
1 2 1
则a[1]%k=1
,a[3]%k=1
,答案即为\(3-1=2\);
3.因为a[i]%k
这个数值第一次出现时,其坐标i
必然为其 最小坐标;
4.所以只需记录第一次a[i]%k
出现的坐标,之后出现相同的a[j]%k
时,更新答案即可;
5.特殊地,对于a[i]%k=0
的情况,由于0%k=0
的特殊性,不需要更新其最小坐标,其最小坐标永远为0
,特判即可;
ps.与std的不同?
进行了3.的小优化,并且在读入时便更新答案,更快更便捷;
- TAG:思维
PZ.cpp
//7-2 小宝的幸运数组
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,n,k,a,f[100005],sum[100005],ans;
int main(){
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&k);
memset(f,0,sizeof(f)); ans=0;
for(int i=1;i<=n;++i){
scanf("%d",&a);
sum[i]=(sum[i-1]+a)%k;
if(sum[i]!=0 && f[sum[i]]==0) f[sum[i]]=i;
//sum[]数组表示a[]%k,f[]数组则记录a[]%k出现的最小坐标
//这里需要特判a[]%k==0的情况,不将其更新,因为f[0]=0为必然
ans=max(ans,i-f[sum[i]]);
}
printf("%d\n",ans==0 ? -1 : ans);
}
return 0;
}
C 上进的凡凡
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 65536K,其他语言131072K
64bit IO Format: %lld
题目描述
凡凡是一个上进的人,他的人生没有下坡路,他也讨厌带有”下坡路“的东西。
所以,对于凡凡来说,只有非降序的数组才是\(nice\)的(如:\(1,2,2,3,4,5,5\));若数组元素个数为\(1\),也满足非降序,也是\(nice\)的。
现在有一个长度为\(n\)的数组,凡凡想知道它的子数组中有多少个数组是\(nice\)的。
你能帮帮他吗?
对于子数组的定义,如果可以通过从开头和从结束分别删除若干个(可以为零或全部,前后删除个数不必相同)元素来从数组\(b\)获得数组\(a\),则称数组\(a\)是数组\(b\)的子数组。(子数组包含原数组,但不包含空串)
输入描述
第一行输入一个整数\(n(1\leqslant n \leqslant 10^5)\),表示数组的长度。
第二行包含\(n\)个空格分隔的整数\(a1,a2,.,an(0 \leqslant ai \leqslant 10^9)\),为数组的元素。
输出描述
输出给定数组的子数组中是\(nice\)数组的个数。(注意使用long long
)
示例1
输入
5 1 2 3 4 5
输出
15
题解
1.一个非降序数组的所有子串都为非降序;
2.只需把给定数组分为若干个非降序部分(每个部分要尽可能长),即从每个\(a_i < a_{i+1}\)的地方断开;
3.然后计算子串数,对于长度为\(n\)的数组子串数为\(\frac{(n+1)n}{2}\),遍历然后相加。
ps.关于 为什么 对于长度为\(n\)的数组子串数为\(\frac{(n+1)n}{2}\)?
参考自兴跃神话的求字符串子串数
ps.1假设数组\(a[\;]=\{1,2,3,4,5\}\);求数组子串可以看成将该数组分割成不同的子数组,需要两个分隔符即可实现。
ps.2\(\{1|2,3|4,5\}\) 设数组长度为\(n\),第一个分隔符有\(n+1\),第二个有\(n\)种放法。
ps.3由于两个分隔符互换位置结果相同,所以需要折半,因为不含空串,所以数组子串个数为\(\frac{(n+1)n}{2}\);
- TAG:思维
std.cpp
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 100005
#define fo1(i,n) for(int i=1;i<=n;++i)
int a[N];
ll f(ll x){
ll ans=((x+1)*x)/2;
return ans;
}
int main(){
int n; cin>>n;
fo1(i,n) cin>>a[i];
ll t=0;
//t代表了上一个非递减子串最后一位的坐标
ll ans=0;
fo1(i,n){
if(a[i]>a[i+1]||i==n){
ans+=f(i-t);
t=i;
}
}
cout<<ans;
return 0;
}
PZ.cpp
//7-3 上进的凡凡
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,a,A;
long long ans,res;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a);
if(a<A){
ans+=(res*(res+1))/2;
res=1;
A=a;
} else{
A=a;
++res;
}
}
ans+=(res*(res+1))/2;
//因为最后一段子串不会在循环中处理,需要在循环结束后处理
printf("%lld",ans);
return 0;
}
D Seek the Joker I
朝野芳乃最可爱了!!!
时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 65536K,其他语言131072K
64bit IO Format: %lld
题目描述
长达数日的春日祭终于告一段落,作为巫女的朝野芳乃在打扫完神社本决定好好享受一下久违的宁静。然而守护了神刀数百年的丛雨难耐寂寞,希望芳乃能陪她一起玩扑克消解愁闷。
芳乃并不擅长市井的游戏,所以总是输多赢少。而昨日被芳乃的神乐舞深深吸引,以致一早就前来建实神社希望能再睹芳华的你碰巧听见了此事。尽管不知道为什么美丽的巫女要自言自语地为玩扑克而苦恼,但你毅然决然地毛遂自荐,希望能为芳乃一解眉间愁。
芳乃告诉了你丛雨准备了\(n\)张扑克牌作为牌堆,每人每次至多从牌堆顶部抽\(k\)张牌,至少抽\(1\)张牌。牌堆底部的最后一张牌作为乌龟,抽中的将输掉这一轮比赛。芳乃想知道在你的帮助下,她和丛雨都采取积极策略时,她自己是否一定能获胜。作为被丛雨邀请的一方,每轮游戏都是芳乃先抽。
因为看不见丛雨而误认芳乃罹患精神分裂的你在不由感叹红颜薄命的同时,决定尽全力帮助芳乃完成她的委托。
声明:本题中的所有角色在剧情发生时均已超过\(18\)岁。
看不到幼刀(丛雨)的我也是屑。
输入描述
第一行包含一个整数\(T\),表示共有\(T\)组测试数据。每组测试数据共一行,包含两个正整数\(n\)和\(k\),分别表示牌堆中有\(n\)张牌和每次抽取最多抽取\(k\)张。数据保证\(T,n,k\leqslant 1000000\)。
输出描述
对于每组测试数据给出一行结果。
如果芳乃必胜,则输出yo xi no forever!
,
否则输出ma la se mi no.1!
。
示例1
输入
4 1 1 23 2 6 4 114 514
输出
ma la se mi no.1! yo xi no forever! ma la se mi no.1! yo xi no forever!
题解
巴什博弈
关于博弈论,可以参考The_Virtuoso的简单易懂的博弈论讲解(巴什博弈、尼姆博弈、威佐夫博弈、斐波那契博弈、SG定理)
PZ's 胡说八道
1.先看6 4
这组数据:
- 如果芳乃先拿\(4\)张,还剩\(2\)张,幼刀拿\(1\)张,还剩\(1\)张,芳乃输;
- 如果芳乃先拿\(3\)张,还剩\(3\)张,幼刀拿\(2\)张,还剩\(1\)张,芳乃输;
- 如果芳乃先拿\(2\)张,还剩\(4\)张,幼刀拿\(3\)张,还剩\(1\)张,芳乃输;
- 如果芳乃先拿\(1\)张,还剩\(5\)张,幼刀拿\(4\)张,还剩\(1\)张,芳乃输;
所以对于6 4
这组数据,芳乃必输(#°Д°)
那么可以确定,6 4
这组数据先手必输,那么可以推理出:
对于x 4
(\(x \in [7,10]\))这样的数据,芳乃可以先手拿牌,让局面变成6 4
而幼刀先手,这样可以确定幼刀必输(๑•̀ㅂ•́)و✧
2.对于11 4
这组数据,芳乃不论怎么拿,都会回到x 4
(\(x \in [7,10]\))这样的数据,这样芳乃会变为后手,而后手必输(#°Д°)
3.再看x 4
(\(x \in [5,2]\))这样的数据,只要芳乃拿牌,让牌堆只剩一张牌,幼刀必输(๑•̀ㅂ•́)و✧
4.寻找规律,可以发现对于x 4
(\(x \in [1,11]\))的数据,只有1 4
、6 4
、11 4
这三组数据芳乃必输(#°Д°)
可以发现,当\(n\%(k+1)==1\)时,芳乃必输(#°Д°)
ps.对于巴什博弈,可以看作没有最后一张牌,这样牌只有\(n-1\)张,可以使用结论:当\(n\)能整除\(k+1\)时先手必败,否则先手必胜;
- TAG:博弈论
std.cpp
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main(){
int T;
cin>>T;
while(T--){
int n,k;
cin>>n>>k;
if((n-1)%(k+1)!=0) cout<<"yo xi no forever!\n";
else cout<<"ma la se mi no.1!\n";
}
return 0;
}
PZ.cpp
//7-4 Seek the Joker I
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,n,k,ans;
int main(){
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&k);
ans=n%(k+1);
if(ans==1) puts("ma la se mi no.1!");
else puts("yo xi no forever!");
}
return 0;
}
E Seek the Joker II
幼刀天下第一!!!
时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
长达数日的春日祭终于告一段落,作为巫女的朝野芳乃在打扫完神社本决定好好享受一下久违的宁静。然而守护了神刀数百年的丛雨难耐寂寞,希望芳乃能陪她一起玩扑克消解愁闷。
芳乃并不擅长市井的游戏,所以总是输多赢少。而昨日被芳乃的神乐舞深深吸引,以致一早就前来建实神社希望能再睹芳华的你碰巧听见了此事。尽管不知道为什么美丽的巫女要自言自语地为玩扑克而苦恼,但你毅然决然地毛遂自荐,希望能为芳乃一解眉间愁。
芳乃告诉了你丛雨准备了\(n\)张扑克牌作为牌堆,自牌顶向下数第\(x\)张牌作为乌龟,即“乌龟”的上方有\(x-1\)张牌,“乌龟”的下方有\(n-x\)张牌,抽中“乌龟”的将输掉这一轮比赛。每人每次可以同时在牌堆顶和牌堆底或者仅在牌堆顶或牌堆底其抽取任意张牌,至少抽\(1\)张牌。但若选择同时在牌堆顶和牌堆底抽牌,则抽牌数量需要相同。芳乃想知道在你的帮助下,她和丛雨都采取积极策略时,她自己是否一定能获胜。作为被丛雨邀请的一方,每轮游戏都是芳乃先抽。
因为看不见丛雨而误认芳乃罹患精神分裂的你在不由感叹红颜薄命的同时,决定尽全力帮助芳乃完成她的委托。
声明:本题中的所有角色在剧情发生时均已超过\(18\)岁。
输入描述
第一行包含一个整数\(T\),表示共有\(T\)组测试数据。
每组测试数据共一行,包含两个正整数\(n\)和\(x\),分别表示牌堆中有\(n\)张牌和”乌龟“的位置。
数据保证,\(T\leqslant 1000000\)和\(n,x \leqslant 3000000\)。
输出描述
对于每组测试数据给出一行结果。
如果芳乃必胜,则输出yo xi no forever!
,
否则输出ma la se mi no.1!
。
示例1
输入
8 1 1 10 3 17 6 12 5 4 3 9 6 12 8 17 11
输出
ma la se mi no.1! yo xi no forever! yo xi no forever! ma la se mi no.1! ma la se mi no.1! ma la se mi no.1! ma la se mi no.1! ma la se mi no.1!
题解
威佐夫博弈
关于博弈论,可以参考The_Virtuoso的简单易懂的博弈论讲解(巴什博弈、尼姆博弈、威佐夫博弈、斐波那契博弈、SG定理)
ps.1对于本题,可以忽略乌龟牌,则可将牌堆视为两堆,一堆为\(n-x\)张,另一堆为\(x-1\)张;
ps.2假设\(x-1<n-x\),只需要
判断\(\lfloor \frac{1+\sqrt{5}}{2}[(n-x)-(x-1)] \rfloor\)是否等于\(x-1\),
判断\(\lfloor \frac{3+\sqrt{5}}{2}[(n-x)-(x-1)] \rfloor\)是否等于\(n-x\)即可;
std代码只判断了第一条,不过也可以;
- TAG:博弈论
std.cpp
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int main(){
double mysticalConstant=(1.0+sqrt(5.0))/2.0;
int T;
cin>>T;
while(T--){
int n,x;
cin>>n>>x;
int a=x-1,b=n-x;
if(a>b) swap(a,b);
int temp=(b-a)*mysticalConstant;
if(temp!=a) cout<<"yo xi no forever!\n";
else cout<<"ma la se mi no.1!\n";
}
return 0;
}
F 成绩查询ing
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
去年的新冠疫情爆发让众多大学生只能只能在家里上学,老师为了方便自己录入成绩和方便大家成绩查询,建立了一个录入和查询成绩的系统,能完成\(M\)次两种不同的查询,输入查询次数\(M\),查询\(M\)次,每次首先输入查询的模式\(T\),\(T\)为\(1\)时,输入同学的姓名\(Name\),并依次输出同学的成绩\(Grade(0 \leqslant Grade \leqslant 100)\), 学号(\(0~1000000\)),性别(\(1/2\)),\(T\)为\(2\)时,输入成绩,输出有具体有哪些同学考到了这个分数,输出同学的\(Name\),并要求按字典序输出,当没有同学为此分数时,则不输出。字典序,对于字符串,先按首字符排序,如果首字符相同,再按第二个字符排序,以此类推。
输入描述
第一行包含一个整数\(N\),表示系统中共有\(N\)个人(\(1\leqslant N \leqslant 100000\))。 下面\(N\)行分别输入\(N\)个人的姓名\(Name\),成绩\(Grade\)(成绩在\(0\sim100\)之间),性别(\(1\)或\(2\)分别表示男性、女性),学号。表示系统中成员的信息 输入查询次数\(M(M \leqslant 10000000)\),接下来\(M\)行完成\(M\)次查询任务
输出描述
输出\(M\)次查询的结果,当\(T\)为\(1\)时,输入同学的姓名\(Name\),并在一行中依次输出同学的成绩\(Grade(0\leqslant Grade\leqslant 100)\), 学号(\(0\sim1000000\)),性别(\(1/2\)),用空格间隔(注意行末无空格),\(T\)为\(2\)时,输入成绩,输出有具体有哪些同学考到了这个分数,输出同学的\(Name\)(每个\(Name\)输出一行,无空格),并要求按字典序输出,当没有同学为此分数时,则不输出。
示例1
输入
5 N 28 2 7475 UN 83 2 27550 EXF 5 2 17298 OVYNH 51 2 14827 XNV 53 1 7591 2 1 XNV 2 27
输出
53 7591 1
题解
1.题目告知查询方式有两种,分别以成绩和名字作为查询下标,因此在读入过程中,可 以此建立相应的数据结构;
2.set s[]
,下标为成绩,存入相应成绩的姓名;
3.mp1,mp2,mp3
,是三个map
,以姓名为下标,分别储存成绩、学号和性别。
- TAG:模拟;STL
std.cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
set<string> s[120];
map<string,int> mp1;
map<string,int> mp2;
map<string,int> mp3;
for(int i=1;i<=n;++i){
string Name;
int xb,xh,cj;
//xb为性别,xh为学号,cj为成绩
cin>>Name>>cj>>xb>>xh;
s[cj].insert(Name);
mp1[Name]=cj;
mp2[Name]=xh;
mp3[Name]=xb;
}
int m;
cin>>m;
while(m--){
int x;
cin>>x;
if(x==1){
string name;
cin>>name;
cout<<mp1[name]<<" "<<mp2[name]<<" "<<mp3[name]<<endl;
}else{
int y;
cin>>y;
set<string>::iterator it;
it=s[y].begin();
for(;it!=s[y].end();it++){
cout<<*it<<"\n";
}
}
}
return 0;
}
G 贪吃的派蒙
你也玩原神吗(。・ω・。)?
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
在遥远的提瓦特大陆上,正在筹备一年一度的羽球节,猎鹿人餐厅为犒劳认真筹备的众人,准备了\(K\)份甜甜花酿鸡供大家排队领取。
在每一次的排队中,编号为i的角色领取上限为\(A_i\),这意味着他可以领取的甜甜花酿鸡在\([1, A_i]\)范围内。当一个角色领完本次的甜甜花酿鸡,他/她就会回到队列的末尾,直到所有甜甜花酿鸡都被吃完为止。当轮到一个角色领取时,如果所有的甜甜花酿鸡都被领完,那么他/她就要帮大家刷盘子。
贪吃的派蒙每次都吃固定的\(A_x\)个甜甜花酿鸡(如果剩下的甜甜花酿鸡的数量比\(A_x\)小,那么他就把剩下的都吃完)。我们很容易找到派蒙的编号,\(A_x\)比其他所有的\(A_i\)都要大。大家都想让派蒙最后留下来刷盘子,请你写一个程序来判断这是否可能。
输入描述
第一行包含一个整数\(T(1\leqslant T\leqslant 100)\),表示有\(T\)组测试数据。接下来每组测试数据包含两行。第一行包含两个整数\(N\)和\(K(2\leqslant N \leqslant 10^5,0\leqslant K \leqslant 10^8)\),分别表示人数和甜甜花酿鸡的数量。第二行包含一个整数\(A_i(1\leqslant A_i \leqslant 10^9)\),表示队列中编号为i的角色可以领取甜甜花酿鸡的最大数量。始终只有一个最大的\(A_x\)。
输出描述
如果大家能找到一种方案让派蒙刷盘子,那么输出YES
,否则输出NO
。
示例1
输入
1 4 3 1 2 3 2
输出
YES
示例2
输入
1 5 8 1 2 3 2 1
输出
No
题解
1.先找出派蒙的位置,计算出它左边的人一轮下来所要吃的最少数量\(A_{min}\)和最多数量\(A_{max}\);
2.另外还要计算出一轮下来除派蒙外其余人所要吃的最少数量和最多数量;
3.如果\(A_{min}\leqslant k\leqslant A_{max}\),那么左边的人肯定能在轮到派蒙吃时正好吃完蛋糕;
4.如果不能的话,那么甜甜花酿鸡就要被派蒙吃掉\(A_x\)个,接下来我们加上一轮下来其余人所要吃的最少数量和最多数量,重复上述步骤判断。
std.cpp
#include<bits/stdc++.h>
#define sc scanf
#define pr printf
#define endl '\n'
#define ll long long
#define lnode node*2,start,mid
#define rnode node*2+1,mid+1,end
#define rep(i,a,b) for(ll i=a;i<=(b);i+=1)
#define input freopen("input.txt", "r", stdin)
#define output freopen("output.txt", "w", stdout)
#define To_string(num,str) {stringstream ss;ss<<num;ss>>str;}
#define To_num(str,num) {stringstream ss;ss<<str;ss>>num;}
const int maxn=(1e5+10);
using namespace std;
struct node
{
int id;
int val;
}b[maxn];
ll a[maxn];
bool cmp(node a,node b)
{
return a.val>b.val;
}
int main()
{
cin.tie(0);cout.tie(0);
ios::sync_with_stdio(false);
//input;
//output;
int t;
cin>>t;
while(t--)
{
ll n,k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[i].id=i;
b[i].val=a[i];
}
sort(b+1,b+1+n,cmp);
int gs=b[1].id;
//gs为派蒙的坐标
ll amin=0,amax=0,bmin=0,bmax=0;
for(int i=1;i<gs;i++)
{
amin+=1;
amax+=a[i];
}
//amin为在派蒙之前的人吃的最少的甜甜花酿鸡
//amax为在派蒙之前的人吃的最多的甜甜花酿鸡
for(int i=1;i<=n;i++)
if(i!=gs)
{
bmin+=1;
bmax+=a[i];
}
//bmin为除派蒙之外的人吃的最少的甜甜花酿鸡
//bmax为除派蒙之外的人吃的最多的甜甜花酿鸡
ll l=(k-amax)/(bmax+a[gs]),r=(k-amin)/(bmin+a[gs]);
//l,r分别为轮到派蒙吃时,甜甜花酿鸡剩余的最少、最多的数量
//实际上它很玄学,鄙人还没有研究明白
if((k-amax)%(bmax+a[gs])!=0) l+=1;
//如果一轮轮完吃不完,那么最少数量应该大于1,所以l+1
if(k>=amin&&k<=amax)
{
cout<<"YES"<<endl;
continue;
}
//如果amin<=k<=amax,说明在派蒙之前的人只吃1个后,派蒙就会把剩余的吃完
if(l<=r&&l>=1)
{
cout<<"YES"<<endl;
continue;
}
//否则,之后轮到派蒙吃的时候,甜甜花酿鸡还有剩余,说明派蒙会把甜甜花酿鸡吃完
cout<<"NO"<<endl;
//都不会,则无法让派蒙留下来刷盘子
}
return 0;
}
/*
4 3
1 2 3 2
5 8
1 2 3 2 1
*/
PZ's Solution
1.与题解的思想相同,不过使用了队列来循环模拟这个过程;
2.需要注意的点是,当一个角色领完本次的甜甜花酿鸡,他/她就会回到队列的末尾,直到所有甜甜花酿鸡都被吃完为止;
- TAG:模拟
PZ.cpp
//7-7 贪吃的派蒙
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int T,n,k,ai,A,ax,L,R;
queue<int>q;
int main(){
scanf("%d",&T);
while(T--){
while(!q.empty()) q.pop();
scanf("%d %d",&n,&k);
ax=L=R=0;
for(int i=1;i<=n;++i){
scanf("%d",&ai);
ai>ax ? ax=ai :233;
q.push(ai);
}
//ax为派蒙的坐标
while(!q.empty()){
A=q.front(); q.pop();
//A为当前人要吃的最多的甜甜花酿鸡的数量
//L为当前所有人吃的甜甜花酿鸡的最少的数量
//R为当前所有人吃的甜甜花酿鸡的最多的数量
if(A==ax&&L<=k&&k<=R&&R+ax>=k){
puts("YES");
break;
} else if(A==ax&&L>k){
puts("NO");
break;
}
if(A==ax) L+=ax; else L+=1;
//派蒙一定会吃ax份甜甜花酿鸡,除非剩余量少于ax份,它也一定会吃完
R+=A;
q.push(A);
}
}
return 0;
}
H 数羊
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 65536K,其他语言131072K
64bit IO Format: %lld
题目描述
憨憨小杨晚上睡不着觉,就开始数羊,她觉得一只一只数太慢了,突发奇想出了一种新的数羊方式,羊羊数量\(A(n,m)\)由两个整形变量\(n\)和\(m\)决定,计算方式如下:
\(\begin{cases}A(1,0)\;=2\\A(0,m)=1 & m \geqslant 0\\A(n,0)\;=n+2 & n \geqslant 0\\A(n,m)=A(A(n-1,m),m-1) & n,m\geqslant 1\end{cases}\)
现在给出\(n\)和\(m\)的值,请你帮小杨数数一共有多少只羊。
输入描述
多组输入。
第一行包含一个整数\(T(1\leqslant T\leqslant 1000)\),表示有\(T\)组测试数据。
每组测试数据包含一行,包含两个整数\(n(1\leqslant n\leqslant 10^9)\)和\(m(0\leqslant m\leqslant 2)\).
输出描述
对每一组输入,在一行中输出\(A(n,m)\)的值,由于输出的结果可能会很大,答案对\(998244353\)取模
示例1
输入
3 3 0 3 1 3 2
输出
5 6 8
题解
暴力打表可知:
\(m=0\)时,\(A(n,0)=n+2\);
\(m=1\)时,\(A(n,1)=2*n\);
\(m=2\)时,\(A(n,2)=2^n\);
注意取模。
证明如下:
\(A(n,m)\)的自变量\(m\)的每一个值都定义了一个单变量函数,例如:
\(m=0\)时,\(A(n,0)=n+2\);
\(m=1\)时,
\(A(1,1,)=A(\underline{A(0,1)},0)=A(\underline{1},0)=2\),则\(\begin{array}{ccc}A(n,1) &=& A(\underline{A(n-1,1)},0)\\ &=&\underline{A(n-1,1)}+2 \\ &=&2*n\end{array}\)
\(m=2\)时,\(A(n,2)=A(A(n-1,2),1)=2A(n-1,2)\),
和\(A(1,2)=A(A(0,2),1)=A(1,1)=2,A(n,2)=2^n\)
- TAG:打表;快速幂
std.cpp
#include<bits/stdc++.h>
#define ll long long
#define lnode node*2,start,mid
#define rnode node*2+1,mid+1,end
#define rep(i,a,b) for(ll i=a;i<=(b);i+=1)
#define input freopen("in.txt", "r", stdin)
#define out freopen("out.txt", "w", stdout)
#define To_string(num,str) {stringstream ss;ss<<num;ss>>str;}
#define To_num(str,num) {stringstream ss;ss<<str;ss>>num;}
const double pi=acos(-1.0);
const int maxn=(5e5+10);
const int inf=0x3f3f3f3f;
const ll mod=998244353;
using namespace std;
ll quick_mod(ll a,ll m)
{
ll ans=1;
while(m!=0)
{
if(m%2==1) ans=ans*a%mod;
a=a*a%mod;
m/=2;
}
return ans%mod;
}
int main()
{
cin.tie(0);
ios::sync_with_stdio(false);
int t;
ll n,m;
//input;
//out;
cin>>t;
while(t--)
{
cin>>n>>m;
ll ans;
if(m==0)
{
ans=n+2;
}
else if(m==1)
{
ans=n*2;
}
else if(m==2)
{
ans=quick_mod(2,n);
}
cout<<ans%mod<<endl;
}
return 0;
}
PZ.cpp
//7-8 数羊
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define P 998244353
int T;
long long n,m;
long long qpow(long long x,long long k){
long long res=1;
while(k){
if(k&1) res=res*x%P;
k>>=1;
x=x*x%P;
}
return res;
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%lld %lld",&n,&m);
if(n==1&&m==0){
printf("2\n");
continue;
} else if(n==0){
printf("1\n");
continue;
} else if(m==0){
printf("%lld\n",n+2%P);
continue;
} else {
if(m==1) printf("%lld\n",n*2%P);
else
printf("%lld\n",qpow(2,n));
}
}
return 0;
}
I 买花
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 65536K,其他语言131072K
64bit IO Format: %lld
题目描述
情人节马上要到了,阳阳想送出\(n\)朵花给喜欢的妹妹,他打算提前开始买。但是,因为他有强迫症,所有的花要分\(k\)天买(\(k>1\),即不能一天全买完),第一天他可以买任意朵花,之后每一天买花的数量为前一天的两倍,(如若第一天买\(4\)朵,第二天就要买\(8\)朵,以此类推)。
现在离情人节还有\(15\)天(\(k\leqslant 15\)),请你告诉阳阳,他能不能刚好买到\(n\)朵花。
输入描述
多组输入。第一行一个正整数\(T(1\leqslant T \leqslant 10^5)\),表示数据组数。
接下来\(T\)行,每行一个正整数\(n(1\leqslant n\leqslant 10^9)\),表示预计买花的数量。
输出描述
每组数据输出一行,共\(T\)行。
判断能否刚好买到\(n\)朵花,可以则输出YE5
,否则输出N0
。
示例1
输入
2 21 20
输出
YE5 N0
题解
1.假设第一天买花\(x\)朵,则一共买花\(x+2x+\cdots+2^kx\)朵,即\(2^{k+1}-1\)朵,又\(1\leqslant k \leqslant 15\);
2.直接枚举\(14\)种情况\(n\)是否能整除\(2^k-1\)。
注意输出,不是
YES
而是YE5
,不是NO
而是N0
!
- TAG:数学
std.cpp
#include<bits/stdc++.h>
using namespace std;
#define ll long long
int main(){
int T;
cin>>T;
while(T--){
bool A=1;
int n;cin>>n;
for(int i=2;i<=16;i++) {
ll x=pow(2,i)-1;
if(n%x==0){
cout<<"YE5\n";
A=0;
break;
}
}
if(A)cout<<"N0\n";
}
}
PZ.cpp
//7-9 买花
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,n,pre[20],sum[20],f;
int main(){
scanf("%d",&T);
pre[1]=sum[1]=1;
for(int i=2;i<=15;++i){
pre[i]=pre[i-1]*2;
sum[i]=pre[i]+sum[i-1];
}
//这里使用了预处理,pre[i]代表了 2^{i-1},
//sum[i]代表了前pre[i]的和,即2^i -1;
//(好像有些赘余,其实可以更简单)
while(T--){
f=0;
scanf("%d",&n);
for(int i=2;i<=15;++i)
if(n%sum[i]==0){
puts("YE5");
f=1;
break;
}
if(!f) puts("N0");
}
return 0;
}
J 这是一题简单的模拟
时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 65536K,其他语言131072K
64bit IO Format: %lld
题目描述
财务计划要从家里出发,去\(N\)个城市出差,然后再回到家中,但\(N\)个出差地点之间不一定都能通车,现在他要筛选出花费最少的路径,你能帮帮他吗?
输入描述
第一行为两个正整数\(N\)和\(M(1\leqslant N \leqslant 300,1\leqslant M \leqslant \frac{N(N+1)}{2})\),分别表示有\(N\)个出差地点和这些地点之间的\(M\)条通路,其中出差地点用\(1\)到\(N\)编号,而财务的家所在城市用编号\(0\)表示。
随后的\(M\)行,每行给出通路连接的两个城市和这条通路上的花费,格式为:
城市A 城市B 花费
通路是双向的,且两个城市间最多有一条通路,不存在自环。保证所有花费大于\(0\)。
再下一行给出一个正整数\(K(K\leqslant 20000)\),表示现在有\(K\)条推荐路径(注意:给出的路径不一定能通过或可能不满足财务的要求)。
接下来\(K\)行每一行表示一个推荐路径,第一个整数\(n\)表示途径的城市数,后面有\(n(n\leqslant 2*N)\)个整数\(x_i(1\leqslant x_i \leqslant N)\) (表示途经的城市(不包括财务的家),如:
3 1 2 3
表示实际路径为\(0→1→2→3→0\)。
输出描述
请你检验给出的\(K\)条推荐路径,当它满足:
1.给出的路径能实际通车,即路径中相邻城市存在通路;
2.给出的路径恰好能都到达\(N\)个出差城市一次,即不能漏掉某个城市,也不能重复到达。
则称这条路径是可行的。
对于给出的\(K\)条推荐路径,请输出其中可行路径中最少的花费,若不存在可行路径,请输出-1
。(题目保证花费和不超过int
范围)
示例1
输入
5 10 0 1 5 0 5 12 1 2 2 2 3 8 3 4 13 1 3 11 0 2 5 0 4 9 4 5 6 3 5 7 5 5 1 3 2 3 1 5 3 2 1 4 5 5 2 1 3 5 4 6 1 2 3 4 5 1 5 1 2 3 5 4
输出
37
题解
本题中\(n\)较小,且为无向图,直接用二维数组建图即可。
遍历推荐路径,寻找最优方案。
注意初始化。
- TAG:图论
std.cpp
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 10005
#define inf 0x3f3f3f3f
#define mem(A) memset(A,0,sizeof(A))
int a[N][N];
int t[N];
bool judge(int n){
for(int i=1;i<=n;i++){
if(t[i]!=1)return 0;
}
return 1;
}
int main(){
int n,m;cin>>n>>m;
for(int i=0;i<=n;i++)for(int j=0;j<=n;j++)a[i][j]=inf;
while(m--){
int x,y,z;
cin>>x>>y>>z;
a[x][y]=a[y][x]=z;
}
int k;cin>>k;
int ans=inf;
while(k--){
int nn;cin>>nn;
mem(t);
bool A=1;
int x=0,y=0,s=0;
for(int i=1;i<=nn;i++){
cin>>y;t[y]++;
if(a[x][y]!=inf){
s+=a[x][y];
}
else A=0;
x=y;
}
if(a[y][0]!=inf){
s+=a[y][0];
}
else A=0;
if(A&&judge(n))ans=min(ans,s);
}
if(ans==inf)cout<<"-1";
else cout<<ans;
return 0;
}
PZ.cpp
//7-10 这是一道简单的模拟
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<climits>
using namespace std;
int N,M,dis[305][305],K,n,vis[305],res,ans=INT_MAX;
bool f;
int main(){
scanf("%d %d",&N,&M);
memset(dis,0x3f,sizeof(dis));
for(int u,v,w,i=1;i<=M;++i){
scanf("%d %d %d",&u,&v,&w);
dis[u][v]=w;
dis[v][u]=w;
}
scanf("%d",&K);
for(int i=1;i<=K;++i){
memset(vis,0,sizeof(vis));
scanf("%d",&n);
int v;
v=f=res=0;
for(int x,i=1;i<=n;++i){
scanf("%d",&x);
res+=dis[v][x];
if(dis[v][x]==0x3f3f3f3f) f=1;
v=x;
++vis[x];
}
res+=dis[v][0];
for(int i=1;i<=N;++i) if(vis[i]==0||vis[i]>1) f=1;
if(!f) ans=min(ans,res);
}
printf("%d",ans==INT_MAX ? -1 : ans);
return 0;
}
K 黑洞密码
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32768K,其他语言65536K
64bit IO Format: %lld
题目描述
近些日子,某科学家接受到了来自外太空的神秘讯息,在经过了一段时间的研究后,科学家发现讯息是一个由字母和数字组成的字符串\(str\),想要破译,需要通过一定的规则将字符串进行转换。规则如下:
1.确定讯息的长度为\(32\);
2.字符串中第\(4n+1\sim4n+4\)的字母和第\(4n+1\sim4n+4(0 \leqslant n \leqslant 3)\)的数字为一组,共\(4\)组;
3.每组的第\(1,2,3,4\)个字符分别往后推每组第\(1,2,3,4\)个数字个数 例:如第一个字母为
a
,第一个数字为3
,转换后变为d
,z
之后是B
,Z
之后是b
;4.将每组内部字母的顺序颠倒;
5..将四组字符合并就是最后的讯息。
输入描述
输入一个长度为\(32\)的字符串
输出描述
输出转换后的密码
示例1
输入
Zzc6Ltw2OD4yR640263W7G8G30HW9C71
输出
RgCgJQwxJfYCDeQG
题解
本题为简单的模拟题目,题目告知每四个字母和数字为一组,利用队列先进先出的特性,用循环遍历字符串,将字母和数字分别存入到两个队列中,进行转换操作。输出答案。
std.cpp
#include<bits/stdc++.h>
using namespace std;
int main(){
string str;
cin>>str;
int len=31;
queue<char> q;
queue<int> s;
for(int i=0;i<=31;i++){
if((str[i]<='z'&str[i]>='a')||(str[i]<='Z'&str[i]>='A')){
q.push(str[i]);
}else
{
s.push(str[i]-48);
}
}
string s1;
while(!q.empty()){
char ss[5];
for(int i=1;i<=4;i++){
ss[i]=q.front();
q.pop();
}
int sss[5];
for(int i=1;i<=4;i++){
sss[i]=s.front();
s.pop();
}
for(int i=1;i<=4;i++){
if(ss[i]>'Z'){
if(ss[i]+sss[i]>'z'){
ss[i]=ss[i]+sss[i]-'z'+'A';
}else
ss[i]+=sss[i];
}else{
if(ss[i]+sss[i]>'Z'){
ss[i]=ss[i]+sss[i]-'Z'+'a';
}else
ss[i]+=sss[i];
}
}
for(int i=4;i>=1;i--){
s1+=ss[i];
}
}
cout<<s1<<endl;
}
PZ's Solution
1.直接按顺序读入,第一个数字对应第一个字母,以此类推;
2.在数组内每\(4\)个一组,分组处理即可;
- TAG:模拟
PZ.cpp
//7-11 黑洞密码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char ch[40],mpZM[40];
int mpSZ[40],ZM,SZ;
char change_chars(char c,int x){
int res=c;
if('a'<=c&&c<='z'){
res+=x;
if(res>'z') res=res-'z'+'A';
} else {
res+=x;
if(res>'Z') res=res-'Z'+'a';
}
char ans=res;
return ans;
}
int main(){
scanf("%s",ch+1);
for(int i=1;i<=32;++i){
if(('A'<=ch[i]&&ch[i]<='Z') || ('a'<=ch[i]&&ch[i]<='z')){
mpZM[++ZM]=ch[i];
} else mpSZ[++SZ]=ch[i]-'0';
}
for(int i=1;i<=16;++i) mpZM[i]=change_chars(mpZM[i],mpSZ[i]);
for(int i=0;i<=3;++i)
for(int j=4*i+4;j>=4*i+1;--j) printf("%c",mpZM[j]);
return 0;
}
L 建立火车站
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld
题目描述
新冠疫情,导致了各个城市之间物资输送的障碍。假设有\(N\)个城市在一条直线上,为了物资能顺利抵达各个城市,可以在路线上建立最多个数为\(K\)个暂时停靠站,由于火车在两个站台(城市也算站台)之间的距离越近,需要的总花费越少,因此我们需要让火车相邻两个站台之间的最大距离最小,求出距离\(L,2 \leqslant N \leqslant 100000, 0 \leqslant K \leqslant 100000\),所有城市坐标小于等于\(10^12\),且不存在负值。提醒: 城市坐标均为正整数,且停靠站只能建在整数坐标点上。
输入描述
第一行输入城市个数\(N\),可建立停靠站个数\(K\),
第二行输入\(N\)个城市的坐标(不保证前一个城市坐标比后一个城市小)。
输出描述
输出\(L\)
示例1
输入
2 2 4 106
输出
34
题解
1.题目需要在一条直线上不同城市(城市算已有站点)之间建立站点,问如何建立站点能使本条路线上最大值达到最小值;
2.若采用暴力的方法,假设最大值为\(1,2,3\)以此类推,时间复杂度过大,因此不行,排除;
3.采用对最大值二分的思路,搜索最大值的最小值。
- TAG:二分答案
std.cpp
#include<bits/stdc++.h>
using namespace std;
const int MAXN=100005;
long long a[MAXN];
int ll,n,k;
long long judge(long long mid)
{
int m=0;
for(int i=2;i<=n;i++)
if(a[i]-a[i-1]>=mid)
{
m+=(a[i]-a[i-1])/mid;
if((a[i]-a[i-1])%mid==0)
m--;
}
if(m>k)
return 0;
return 1;
}
int main()
{
cin>>n>>k;
for(int i=1;i<=n;i++)
cin>>a[i];
sort(a+1,a+1+n);
long long l=0;
long long r=1000000000000;
while(l<r)
{
long long mid=(l+r)/2;
if(judge(mid)==1)
r=mid;
else
l=mid+1;
}
cout<<l<<endl;
return 0;
}
PZ.cpp
//7-12 建立火车站
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int n,k,usek,nowi;
long long a[100005],L,R,MID,nowdis,needdis,ans;
bool check(long long dis){
nowdis=a[1]; nowi=1; usek=0;
while(nowdis<a[n]&&nowi<n){
needdis=a[nowi+1]-a[nowi];
if(needdis<=dis) ++nowi;
else {
usek+=needdis/dis;
if(needdis%dis==0) --usek;
if(usek>k) return 0;
++nowi;
}
nowdis=a[nowi];
}
return 1;
}
int main(){
scanf("%d %d",&n,&k);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
sort(a+1,a+1+n);
R=a[n]-a[1];
L=1;
while(L<=R){
MID=L+R>>1ll;
if(check(MID)) ans=MID,R=MID-1;
else L=MID+1;
}
printf("%lld",ans);
return 0;
}
赛后感言
比完赛推gal的我也是屑
1.本身难度并不高,可能因为只是校赛级别的缘故(当然,清华校赛那另说,我现在都头疼)
2.题目大多并没有考察过多的算法,反而是考察了很多技巧和基础知识;
3.A题上来就给了我一个下马威,因为我没看到有spj,而且题目描述有一定的迷惑性(就是那个合并的举例啊!!!误导人啊!!!ヽ(≧□≦)ノ
4.B题、C题更像是一对亲兄弟,但是B题显然比C题要难一些,有关于整除\(k\)的内容,还要感谢高中时GJH(zùn zùn)的指点;
5.D题、E题纯粹考察博弈论知识点,不过OIer人均二次元果然是真的,对吧!loli控yyh!
6.F题考试时没有想到用set,导致超时着实有些可惜,STL本身还是比较方便的,本题就着重考察了STL的应用,当然,如果用 hash+堆 手写数据结构 的大神当我没说;
7.G题数据好像有些水,牛客上有人反映了一下;
8.H题其实有些应试那味儿了(当然,递推大佬当我没说),打表还是算法竞赛中找规律的好帮手,至于推不推荐这种做法,还要看自己的追求了;
9.J题虽然考了图论,但没有考最短路或者最小生成树,着实可惜(连邻接表都没有考察),可能是为了迎合难度吧;
10.K题的题面 纯属吓唬人,其实题目本身并不复杂,但是题目描述让人头疼还是值得吐槽的;
11.L题是二分答案,也算算法竞赛中的基础题型了,其实算法和数据结构都是固定的,但算法竞赛的精华题型反而是这种比较基础的东西,因为其他华丽的东西很可能是搭建在这种基础题型上的。
12.虽然考察比较基础,但自己毕竟没有AK,感觉有点小失落,可能因为推了星奏有点伤神新岛夕,不愧是你,但鹰国人永不脱鸥!星奏会回来的!(T_T)
ps.有PZ.cpp代表了考试时鄙人做了出来,没有则反之,代码均为考场代码(除了B题代码不小心丢失重写了)