寒假训练第六周
寒假训练第六周
第一天
蓝桥杯训练
斐波拉契数组
大意:
给定数组A,为最少修改几个元素后,数组A会变成一个斐波拉契数组。
思路:
斐波那契数组在第 30 项左右就超过 1e6了。又因为所有 ai 都小于等于 1e6,所以在 30 项以后的 ai 都必须修改。现在我们只需要考虑前 30 项是否可以不改了。如果确定了 a0 的值,就可以确定整个斐波那契数组,当 0>a0>1e6 则都需要改,所以我们只需要在 1∼1e6 中枚举 a0,从而算出整个斐波那契数组。然后看最多有多少个数不需要改即可。
代码:
#include<bits/stdc++.h> // 下标从 1 开始using namespace std;
int n; int a[100010];
int f[60];
int main(){
cin>>n; for(int i=1; i<=n; ++i) cin>>a[i];
int ans=n;
for(int i=1; i<=1e6; ++i)
{
int nowans=max(n-31,0);
f[1]=f[2]=i;
for(int j=3; j<=min(n,31); ++j)
{
if(f[j-1]+f[j-2]>1e6)
{
for(int k=j; k<=min(n,31); ++k) f[k]=0;
break;
}
f[j]=f[j-1]+f[j-2];
}
for(int j=1; j<=min(n,31); ++j) nowans+=(f[j]!=a[j]);
ans=min(ans,nowans);
}
cout<<ans;
return 0;
}
C.求和
大意:
给定n个整数,求他们两两相乘再相加的和。
思路:
列出算式,分组,
求前面所有的数的和 × 这个位置上的数
代码:
#include<bits/stdc++.h>using namespace std;
int n,a;
long long tmp,ans;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a),ans+=a*tmp,tmp+=a;//tmp求前面所有的数的和,ans累加答案
printf("%lld",ans);
return 0;
}
H.卡牌
大意:
n种卡牌,给出mei种的张数和可以补的张数,还有总共能补的张数,问进行补牌操作后,能凑出多少套牌。
思路:
二分答案,
判断x能否满足条件
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=200010;
//二分答案
LL n,m;
int a[N],b[N];
bool check(int x){
LL v=m;
for(int i=1;i<=n;++i){
//直接符合
if(a[i]>=x) continue;
//肯定不符合
if(a[i]+b[i]<x) return false;
if(a[i]+b[i]>=x&&v>=x-a[i]){
v-=(x-a[i]);
}else{
return false;
}
}
return true;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i) cin>>b[i];
int l=0,r=N*2;
while(l<r){
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
cout<<r<<endl;
return 0;
}
第二天
强迫症的lpl
大意:
n个长度不同的名字,每次将其中的n-1个名字加一个字,问最少经过多少次操作能让全部的名字长度相同。
思路:
先考虑最简单的情况,最小长度的名字一定要增加到最大长度的名字。
答案是每个长度-最小长度。
考虑每次操作除最大长度的名字外,把其他名字都当作最小长度,
一次操作完后,每个名字与最小长度的差值成为新的目标。
代码:
#include<iostream>
#include<sstream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn=1000005;
int t,n,m,a[maxn],minn;
int main()
{
scanf("%d",&t);
while(t--)
{
minn=233333333;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
minn=min(minn,a[i]);
}
ll ans=0;
for(int i=0;i<n;i++)
ans+=a[i]-minn;
cout<<ans<<endl;
}
return 0;
}
Grandma Laura and Apples
大意:
n个顾客,单价p,每个顾客买老奶奶所拥有的一半苹果,当剩余苹果个数为奇数时,老奶奶会赠送半个苹果,最后苹果卖光,问总共卖了多少钱。
思路:
模拟倒推回去
代码:
#include<bits/stdc++.h>
using namespace std;
ll n,p,sum,a;//数据太大注意开 long long;
string s[maxn];
int main()
{
n=read();p=read();
for(int i=1;i<=n;i++)cin>>s[i];
for(int i=n;i>=1;i--)
{
a*=2;
if(s[i]=="halfplus")a+=1;
sum+=a;//每一次的a中有一半是自己新买的(送半个的话另外半个是自己花钱买的)
}
cout<<sum*p/2<<endl;//sum中有一半是花钱买的,所以sum*(p/2)乘单价的一半
return 0;
}
第三天
cf训练
G.New Game
大意:
已知每场比赛两名选手的输赢结果,求游戏最后的排名。
游戏的排名规则为如果 A 赢 B, B 赢 C 则最后排名为 A, B, C。
思路:
拓扑序,
入度+优先队列
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=1e3+10;
int input[N];
bool vis[N];
vector<int> g[N];
vector<int> ans;
priority_queue<int,vector<int>,greater<int>> q;
int main()
{
int tt;scanf("%d",&tt);
while(tt--){
for(int i=1;i<N;i++){
input[i]=0;
vis[i]=false;
g[i].clear();
}
int n,m;scanf("%d%d",&n,&m);
for(int i=0;i<m;i++){
int a,b;scanf("%d%d",&a,&b);
input[b]++;
g[a].push_back(b);
}
for(int i=1;i<=n;i++){
if(input[i]==0){
q.push(i);
vis[i]= true;
}
}
ans.clear();
while(q.size()){
int t=q.top();q.pop();
ans.push_back(t);
for(int i=0;i<g[t].size();i++){
if(vis[g[t][i]])continue;
input[g[t][i]]--;
if(input[g[t][i]]==0){
q.push(g[t][i]);
vis[g[t][i]]= true;
}
}
}
for(int i=0;i<ans.size();i++){
printf("%d",ans[i]);
if(i!=ans.size()-1)printf(" ");
}
printf("\n");
}
return 0;
}
第四天
每日一题
导弹拦截
大意:
给出所有导弹的坐标,确定两套系统的最小半径平方和。
思路:
以距1号系统的距离进行升序排序。然后从距离1号系统最远的那枚导弹开始,计算出比它距离1号系统的距离远的所有导弹中距离2号系统的距离最远的那个距离。
代码:
#include<iostream>#include<cstring>#include<algorithm>#include<cmath>using namespace std;
int x11,y11,x22,y22,n,minn=4000010;
struct DI
{
int d1,d2,i;//d1、d2分别为于1、2号系统的距离,i为该导弹的初始编号
}di[1000010];//导弹的信息bool cmp(DI a,DI b){
return a.d1<b.d1;
}//不会写重载运算符,只能这样写了,排序比较int main(){
cin>>x11>>y11>>x22>>y22>>n;
int x[n+5],y[n+5];//存储每枚导弹的坐标
memset(di,0,sizeof(di));//初始化
for(int i=1;i<=n;i++)
{
cin>>x[i]>>y[i];
di[i].d1=pow(x[i]-x11,2)+pow(y[i]-y11,2);//计算该枚导弹与1号系统的距离
di[i].i=i;//存储该枚导弹的初始编号
}
sort(di+1,di+n+1,cmp);
for(int i=n;i>0;i--)
{
int a;
a=pow(x[di[i].i]-x22,2)+pow(y[di[i].i]-y22,2);//计算该枚导弹与2号系统的距离
di[i].d2=max(a,di[i+1].d2);//比较,若比后面所有导弹的距离都大,则替换
}
for(int i=0;i<=n;i++)
{
int a;
a=di[i].d1+di[i+1].d2;//计算以该枚导弹为1号系统拦截的最远导弹所需的代价
minn=min(a,minn);
}//枚举答案,取最小值
cout<<minn;//输出
return 0;
}
K倍区间
大意:
给定长度为n的数列,若其中一段连续的子序列之和是K的倍数,则这个区间为K倍区间,求数列中有多少个K倍区间。
思路:
前缀和+同余定理
同余定理:如果两个整数 a 和 b,(a-b)%m == 0 , 那么 a%m 和 b%m 的余数相等 。
代码:
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;
int a[100010];
int s[100010];
int num[100010];
int main()
{
int n,k;
while(~scanf("%d%d",&n,&k))
{
memset(s,0,sizeof(s));
long long ans=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s[i]=(a[i]+s[i-1])%k;
ans+=num[s[i]];
num[s[i]]++;
}
printf("%lld\n",ans+num[0]);//
}
return 0;
}
第五天
cf
E.计算最小值
大意:
n个数组,每组m个数,
从每个组里取一个数构成一个集合,使极差最小。
思路:
对每一行的数据进行排序,将所有的数放到一个数组b中,对整个数组再进行一次排序。
在数组b中枚举起点,然后从小到大看每一行的数据中最小一个大于等于起点的数,记录这些数的答案。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=1e6+10;
int n,m;
int a[12][N];
int b[N],c[12];
int res=1e9;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i=1;i<=n;i++) c[i]=1;
int f=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
b[++f]=a[i][j];
}
}
for(int i=1;i<=n;i++) sort(a[i]+1,a[i]+m+1);
sort(b+1,b+n*m+1);
for(int i=1;i<=n*m;i++){
int mx=0,ok=1;
for(int j=1;j<=n;j++){
while(c[j]<=m&&a[j][c[j]]<b[i])++c[j];
if(c[j]>m){
ok=0;
break;
}
mx=max(mx,a[j][c[j]]-b[i]);
}
if(!ok)break;
res=min(res,mx);
}
cout<<res;
return 0;
}
G.电子表校对
大意:
输入为字符串式的时间,计算出差值。
思路:
getline(cin,s)读入整行
s.substr(pos,num)截取子字符串
代码:
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int N=1e6+10;
map<string,int> mp;
map<int,string> pm;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
mp[" _ | ||_|"] = 0; pm[0] = " _ | ||_|";
mp[" | |"] = 1; pm[1] = " | |";
mp[" _ _||_ "] = 2; pm[2] = " _ _||_ ";
mp[" _ _| _|"] = 3; pm[3] = " _ _| _|";
mp[" |_| |"] = 4; pm[4] = " |_| |";
mp[" _ |_ _|"] = 5; pm[5] = " _ |_ _|";
mp[" _ |_ |_|"] = 6; pm[6] = " _ |_ |_|";
mp[" _ | |"] = 7; pm[7] = " _ | |";
mp[" _ |_||_|"] = 8; pm[8] = " _ |_||_|";
mp[" _ |_| _|"] = 9; pm[9] = " _ |_| _|";
string r1,r2,r3;
int a[8],b[8],c[8];
int h1, m1, s1, t1, h2, m2, s2, t2, d, hd, md, sd;
getline(cin, r1);
getline(cin, r2);
getline(cin, r3);
for (int i = 0; i < 6; ++i) {
string x = r1.substr(i * 3, 3) + r2.substr(i * 3, 3) + r3.substr(i * 3, 3);
a[i] = mp[x];
}
getline(cin, r1);
getline(cin, r2);
getline(cin, r3);
for (int i = 0; i < 6; ++i) {
string x = r1.substr(i * 3, 3) + r2.substr(i * 3, 3) + r3.substr(i * 3, 3);
b[i] = mp[x];
}
s1 = a[4] * 10 + a[5]; s2 = b[4] * 10 + b[5];
m1 = a[2] * 10 + a[3]; m2 = b[2] * 10 + b[3];
h1 = a[0] * 10 + a[1]; h2 = b[0] * 10 + b[1];
t1 = s1 + m1 * 60 + h1 * 3600;
t2 = s2 + m2 * 60 + h2 * 3600;
if (t1 == t2) {
cout << "gang gang hao\n";
hd = md = sd = 0;
}
else if (t1 < t2) {
cout << "late\n";
d = t2 - t1;
hd = d / 3600; d -= hd * 3600;
md = d / 60; d -= md * 60;
sd = d;
}
else {
cout << "early\n";
d = t1 - t2;
hd = d / 3600; d -= hd * 3600;
md = d / 60; d -= md * 60;
sd = d;
}
c[0] = hd / 10; c[1] = hd % 10;
c[2] = md / 10; c[3] = md % 10;
c[4] = sd / 10; c[5] = sd % 10;
string x;
for (int i = 0; i < 3; ++i) {
x = "";
for (int j = 0; j <= 5; ++j) {
x += pm[c[j]].substr(i * 3, 3);
}
cout << x << '\n';
}
return 0;
}
D.转动命运之轮
大意:
n个小朋友各自有开心值hi,
对于某种分配玩具下,小朋友 i 会首先观察拿到的玩具的标号,记为 wi。下面会进行若干轮操作,每轮操作后小朋友 i 会观察此时他的玩具标号 pi,并将这个玩具给小朋友 wi。经过此轮操作后,若发现某个小朋友 i 的玩具恰好等于初始拿到的玩具标号,即 pi=wi,则该小朋友不再进行接下来的所有操作。定义总开心值为每个小朋友的开心值乘一共经历的轮数的和,形式化表达:H=∑i=1nhi×ci
现在定义 Un 为长度 n 的排列的全集,你需要输出:∑P∈UnHP
思路:
根据排列的性质,如果是统计所有排列的情况,那么所有排列进行完后,每一个人传递玩具的总次数一定是相同的,如果题目中给出的快乐值之和我们设为x,那么答案一定是一个系数p和x的乘积。
然后模拟n = 1 , 2 , ⋯ , 5 发现系数p的值分别等于1 , 3 , 12 , 60 , 360 。
代码:
#include <bits/stdc++.h>
using namespace std;
#define LL long long
#define endl '\n'
const int N=1e6+10;
const LL mod = 998244353;
LL n, sum = 0, tmp = 1;
LL a[2005];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
sum = (sum + a[i]) % mod;
}
for (int i = 2; i <= n; ++i) {
tmp = (tmp * (i + 1)) % mod;
}
cout << (sum * tmp) % mod;
return 0;
}
第六天
黑板上的数
大意:
黑板上有3个初始数字,每一次可任选2个不同数字,计算二者之差的绝对值,如果这个绝对值不在黑板上,就把它写到黑板上,问最多能添加多少个数字。
思路:
写几组后发现最后黑板上出现的是以最开始三个数的最大公约数为公差的等差数列,所以只要计算出3个数的公约数,再用原来最大的那个数除以最大公约数,减去三就是最多能添加的数量。
代码:
#include<bits/stdc++.h>
using namespace std;
long long gcd(long long a,long long b)
{
return b==0?a:gcd(b,a%b);
}
int main()
{
int n;
scanf("%d",&n);
for(int i=0; i<n; i++)
{
long long x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
long long num=gcd(gcd(x,y),z);
long long result=((max(max(x,y),z))/num)-3;
printf("%lld\n",result);
}
return 0;
}
菜菜k的序列
大意:
给定一个序列,从序列里任选3个数使和为0,问共有多少种选法。
思路:
先算两数之和,再求相反数
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
int t,a[maxn],n,ans,sum[5000000],temp;//数组应该开大一些题中a是2e5要注意数据否则容易RE
int main()
{
cin>>t;
while(t--)
{
cin>>n;
ans=0;
memset(a,0,sizeof(a));
memset(sum,0,sizeof(sum));
for(int i=0;i<n;i++)
cin>>a[i];
sum[500000+a[0]]++;
for(int i=1;i<n;i++)//i=0时做无用功;必须三个数相加
{
for(int j=i+1;j<n;j++)
{
temp=a[i]+a[j];//先算两数之和;
ans+=sum[500000-temp];//再求相反数即可;
}
sum[500000+a[i]]++;//三个数加一块,不能算上自己,所以要在自己遍历完之后再++
}
cout<<ans<<endl;
}
return 0;
}
做计数
大意:
求有多少个不同的正整数三元组(i,j,k)满足根号i+根号j=根号k
思路:
两边平方,
当i,j都是整数且i,j为完全平方数时才会对应一个k。
枚举1到n中所有的完全平方数(完全平方数可以表示为 x^2 ,只需要枚举 x ,再枚举这个完全平方数的因数,统计答案。)
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e5+7;
const ll mod=1e9+7;
ll n,ans;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i*i<=n;++i)
{
for(int j=1;j<i;++j)
if(i*i%j==0)ans+=2;
ans++;
}
cout<<ans<<endl;
return 0;
}