A - Sasha and His Trip
在第一个城市加满,之后每到一个城市加一升油,直到满足最低油耗量。注意有可能在第一个城市就超过最低油耗量。
#include <bits/stdc++.h>
using namespace std;
int main (){
int n,v;
cin>>n>>v;
int ans=min(n-1,v);
int need=n-1-ans;//还需要的油
ans+=(1+need+2)*need/2;//等差求和公式
printf("%d\n",ans);
}
B - Emotes
使用k次最大值的表情,再使用一次第二大值的表情,循环直到m次。
#include <bits/stdc++.h>
using namespace std;
long long a[200004];
int main (){
int n,m,k;
cin>>n>>m>>k;
for (int i = 0; i < n; ++i) {
cin>>a[i];
}
long long ans=0;
sort(a,a+n);
long long max1=a[n-1];
long long max2=a[n-2];
ans+=m/(k+1)*(k*max1+max2);//把m分为长度为(k+1)的组
ans+=m%(k+1)*max1;//剩余的
printf("%lld\n",ans);
}
C - Kayaking
枚举划单人船的两个人,其余的排序后依次上船即可
#include <bits/stdc++.h>
using namespace std;
int a[104];
int main (){
int n;
cin>>n;
for (int i = 1; i <= 2*n; ++i) {
cin>>a[i];
}
sort(a+1,a+1+2*n);
int ans=1e9;
for (int i = 1; i <= 2*n; ++i) {//枚举第一个单人船的人
for (int j = i+1; j <= 2*n; ++j) {//第二个
int sum=0;
for (int k = 1; k <= 2*n; k+=2) {
while(k==i||k==j)k++;//跳过单人船的人
int b=k+1;
while(b==i||b==j)b++;//跳过单人船的人
if(b<=2*n)
sum+=a[b]-a[k];
}
ans=min(ans,sum);
}
}
printf("%d\n",ans);
}
D - Got Any Grapes?
先把绿葡萄给Andrew,如果不够的话输出NO,够的话绿葡萄减去x个。
再把绿葡萄和紫葡萄给Dmitry,同上。
最后把所有葡萄给Michal,同上。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int x,y,z,a,b,c;
cin>>x>>y>>z>>a>>b>>c;
a-=x;
if(a<0){
puts("NO");
return 0;
}
if(a+b<y){
puts("NO");
return 0;
}
int sum=a+b+c-y;
if(sum<z){
puts("NO");
return 0;
}
puts("YES");
}
E - Clarke and food
排序,然后从小到大遍历即可。
#include "bits/stdc++.h"
using namespace std;
int a[100005];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,v;
scanf("%d%d",&n,&v);
for (int i = 0; i < n; ++i) {
scanf("%d",&a[i]);
}
sort(a,a+n);
int ans=0;
for (int i = 0; i < n; ++i) {
v-=a[i];
if(v<0)break;
else ans++;
}
printf("%d\n",ans);
}
}
F - Two Cakes
每个值出现两次,即每个值对应两个下标,因为必须按照1-n的顺序走,那么每次选择对后面的结果都没有影响。对于每种蛋糕店只有两种方案,每次取最小值即可。详见注释。
#include "bits/stdc++.h"
using namespace std;
int a[200004];//用来记录蛋糕店的下标
int b[200004];
int main()
{
int n;
scanf("%d",&n);
int x;
memset(a,0, sizeof(a));
memset(b,0, sizeof(b));
for (int i = 1; i <= 2*n; ++i) {
scanf("%d",&x);
if(a[x]==0)a[x]=i;//a[x]==0说明x这种蛋糕店是第一次出现
else {//否则就是第二次出现
b[x]=i;
}
}
long long ans=0;
a[0]=1,b[0]=1;//初始位置是1
for (int i = 1; i <= n; ++i) {
//两种方案,第一种a[i-1]到a[i]和b[i-1]到b[i],第二种a[i-1]到b[i]和b[i-1]到a[i]
ans+=min( abs(a[i]-a[i-1]) + abs(b[i]-b[i-1]) , abs(a[i]-b[i-1]) + abs(b[i]-a[i-1]) );
}
printf("%lld\n",ans);
}
G - Sushi for Two
找连续相等的部分,然后然后与上一个连续相等的部分组合,求一个max即可。
#include<bits/stdc++.h>
using namespace std;
int a[100004];
int main()
{
int n;
cin>>n;
a[0]=0;
a[n+1]=0;
for (int i = 1; i <= n; ++i) {
scanf("%d",&a[i]);
}
int ans=0;
int laslen=0;//上一个连续相等的部分
int nowlen=1;//现在连续相等的部分
for (int i = 2; i <= n+1; ++i) {
if(a[i]==a[i-1])nowlen++;//如果现在这个与上一个一样,那么还是连续的
else//否则不连续,进行计算
{
ans=max(min(nowlen,laslen),ans);//由题意
laslen=nowlen;//更新上一个连续相等的部分
nowlen=1;
}
}
printf("%d\n",ans*2);
}
H - Nastya Is Buying Lunch
朴素的想法,想要将a[i]前移,首先看能否和a[i-1]交换,若不行,再看a[i-2]与a[i-1],若a[i-2]能同时与a[i-1]和a[i]交换,那么能将a[i-2]移到a[i]后面,a[i-1]和a[i]整体前移一位。
考虑普遍的情况,对于i到j,如果a[i]到a[j],都可以和a[i-1]交换,那么就可以将a[i-1]移到a[j]的后面,将a[i]到a[j]整体前移一位。
然而暴力求交集是不可行的,我们可以从v到u建单向边,用一个num[x]数组,表示x后面与x有边的点的数量,因为位移是整体前移,所以每个点对前面的贡献不会消失。
当num[x]=x到i的距离时,可以将x移到i后面,并且x不对前面的点有贡献。
整体复杂度O(n+m) 详见注释
#include <bits/stdc++.h>
using namespace std;
int a[300004];
int num[300004];//记录num[a[i]]后面的人可以和a[i]交换的人的个数
vector<int>v[300004];//v[i]维护能与i交换的点
int main (){
int n,m;
cin>>n>>m;
for (int i = 1; i <= n; ++i) {
scanf("%d",&a[i]);
}
int x,y;
while(m--)
{
scanf("%d%d",&x,&y);
v[y].push_back(x);
}
memset(num,0, sizeof(num));
for (int i = 0; i < v[a[n]].size(); ++i) {//先计算n位置的贡献
num[v[a[n]][i]]++;
}
int ans=0;
for (int i = n-1; i >=1 ; --i) {
if(num[a[i]]==n-ans-i)ans++;//整体前移一位
else
{
for (int j = 0; j < v[a[i]].size(); ++j) {//否则计算该点的贡献
num[v[a[i]][j]]++;
}
}
}
printf("%d\n",ans);
}