2021级新生个人训练赛第36场

问题 A: 礼物

题目描述
在一个n×n的网格图上,放置着m个礼物,每个礼物有一个价值vi(1≤i≤m),你可以选择一个礼物,然后选择:
(1)取走与它同列的所有礼物,或者(2)取走与它同行的所有礼物
请问所能获取的礼物的最大价值之和是多少?
输入
第一行两个正整数n,m。
之后m行,每行三个整数xi,yi,vi,表示第i个礼物在第xi行,第yi列的格子上(不同礼物可能会在同一个格子),其价值为vi。
输出
一个整数,表示能获取的最大的礼物价值之和。
样例输入 Copy
6 7
1 3 1
2 2 2
2 4 4
3 3 6
3 6 3
5 2 5
5 4 6
样例输出 Copy
11
提示
样例1解释
在这里插入图片描述

选择第5行的任意一个礼物,然后将第5行取完。
【数据范围】
对于40%的数据,1≤n,m≤100
对于70%的数据,1≤n,m≤1000,0<vi≤10000
对于100%的数据,1≤n≤1000,1≤m≤105,1≤xi,yi≤n,vi≤106

类似一开始学C的时候鞍点那题,不是当我胡说 ,签到题过的人很多就不讲了直接放代码

#include<iostream>
using namespace std;
int a[1001][1001];
long long n,m,x,y,v,maxn=-1;
int main(){
    cin>>n>>m;
    while(m--)
    {
        cin>>x>>y>>v;
        if(a[x][y])
            a[x][y]+=v;
        else
            a[x][y]=v;
    }
    for(int i=1;i<=n;i++)
    {
        long long int sum=0;
        for(int j=1;j<=n;j++)
        {
            sum=sum+a[i][j];
            maxn=max(maxn,sum);
        }
    }
    for(int j=1;j<=n;j++)
    {
        long long int ans=0;
        for(int i=1;i<=n;i++)
        {
            ans=ans+a[i][j];
            maxn=max(maxn,ans);
        }
    }
    cout<<maxn<<endl;
    return 0;
}

问题 B: 数字游戏

题目描述
有一天,小明给佳佳出了一道题,
给出一个正整数n,佳佳可以进行如下三种操作:
1、使n减去1
2、如果n是2的倍数,使n除以2
3、如果n是3的倍数,使n除以3
问佳佳最少可以通过几步操作,将n变为0。
为了考验佳佳的知识水平,小明给出了T个数字n1,n2,…,nT,让佳佳对每个数字都给出答案。然而佳佳一心只想着晚上吃啥,想让聪明的你来帮助他解决这个问题,并答应解决后请你吃饭,于是你义不容辞地接下了这个任务。
输入
第一行一个正整数T,表示总共有T个数字;
接下来T行,每行一个正整数ni
输出
共T行,每行一个数字,第i行为对于ni的答案。
样例输入 Copy
2
7
10
样例输出 Copy
4
4
提示
【样例解释】
7->6->2->1->0
10->9->3->1->0

【数据范围】
对于40%的数据,T≤10,ni≤30
对于70%的数据,T≤20,ni≤1000
对于100%的数据,T≤20,ni≤1000000

思路:暴力 提供两种思路1.对于每个T做BFS时间复杂度为T*1e6
2.初始化1e6以内所有数的最少操作,这个可以用递推来写
这里我写的是BFS,不了解BFS可以先学;递推也很简单因为每次i都和i-1和/2 或者/3有关系,那么我们可以这么写
在这里插入图片描述

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
# include<bits/stdc++.h>
# include<unordered_map>
 
 
# define eps 1e-9
# define fi first
# define se second
# define ll long long
# define int ll
// cout<<fixed<<setprecision(n) 
//bool operator<(const Node& x )
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int > PII; 
const int mod=998244353;
const int N=1e6+10;
const int Time=86400;
const int X=131;
const int inf=0x3f3f3f3f;
const double PI = 1e-4;
double pai = 3.14159265358979323846; 
 
int T,n,m,k,ans,sum,tt,root,x,y;
int a[N],b[N];
struct Node{
    int x,step;
};
int bfs(int n){
       queue<Node>q;
       q.push({n,0});
       while(q.size()){
            auto now = q.front();
            q.pop();
            if(now.x == 0){
                  return now.step;
               }
            if(now.x%3 == 0) q.push({now.x/3,now.step+1});
            if(now.x%2 == 0) q.push({now.x/2,now.step+1});
            q.push({now.x-1,now.step+1});
       }
}
void solve(){
         cin >> n;
         cout<<bfs(n)<<"\n";
} 
/*
 
11
*/
signed main(){  
    std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 
    cin >> T;
    // T = 1;
    while(T--){
        solve();
    } 
    return 0; 
}

问题 C: 苹果gcd

题目描述
有n筐苹果,第i筐苹果中有ai个苹果,现在你可以吃掉任意一筐里面的苹果,但是由于食量有限,不能吃超过k个苹果。
请问,如何让这n筐中苹果数量的最大公约数尽可能大。并输出这个最大公约数。
输入
第一行是两个整数n和k,表示苹果筐数和吃掉苹果个数的上限。
第二行包含n个整数a1,a2,…aN,表示第1,2,…N个筐内的苹果数。
输出
输出n筐中苹果数量最大的最大公约数。
样例输入 Copy
【样例1】
6 10
5 6 7 8 9 10
【样例2】
10 3
10 10 10 10 10 10 10 10 10 14
样例输出 Copy
【样例1】
5
【样例2】
2
提示
【样例1解释】
第二筐吃掉1个,第三框吃掉2个,第四筐吃掉3个,第五筐吃掉4个,一共吃掉10个苹果,最大公约数为5。
【样例2解释】
一个苹果也不吃,最大公约数为2。

【数据范围】
对于30%的数据,1<=n<=1000
另有30%的数据,ai<=1000
对于100%的数据,1<=n<=1000000,ai<=1000000,k<=109

思路:第一眼看上去像二分,但是易证这不满足单调性,或者也可以这么说不能二分最大公约数,只能二分因子,我们考虑分块来写:关键是值域最大1e6,所以可以先做一遍前缀和,对最大公约数枚举倍数,对于相邻的两个倍数之间的数字肯定会减到较小的那个倍数,然后用这一段的区间和减去较小的那个倍数;一段里的总和-(j-i)当前段的长度等于这段区块要吃的个数;这样的话是可以用o(1)的复杂度把这块要吃的个数全算出来,所以应该先把所有数排好序,然后我们去枚举最大公因数是多少,之后按1-x,x+1-2x,…依次类推把每个块要吃多少算出来

#include <iostream>
#include <cstring>
#include <algorithm>
#include<stdio.h>
#define int long long
using namespace std;
int n,m;
int ai[2000010];
int si[2000010];
int s2[2000010];
signed main()
{
    cin >> n>>m;
    int qi=0;
    ai[n+1]=1e9;
    for(int i=1;i<=n;i++) scanf("%lld",&ai[i]),qi=max(qi,ai[i]),si[ai[i]]++;
    sort(ai+1,ai+1+n);
    for(int i=1;i<=1e6;i++) si[i]=si[i-1]+si[i];
    for(int i=1;i<=n;i++)s2[i]=s2[i-1]+ai[i];
    int res=-0x3f3f3f3f;
    for(int i=1;i<=qi;i++)
    {
        int last=0;
        int ans=0;
        for(int j=i;j<=qi+i;j+=i)
        {
            int x=j-1;
            int laze=si[x]-si[max(x-i,(int)0)];
            int l=last+1,r=last+laze;
            int sum=s2[r]-s2[l-1];
            if(j==i)ans+=sum;
            else ans+=sum-(j-i)*laze;
            last=r;
        }
        if(ans<=m)res=max(res,i);
    }
    cout << res<<endl;
    return 0;
}

问题 D: 通关游戏

题目描述
A班和B班的同学最近迷上了一个拥有几乎无限关卡(第0关到第109关)的通关游戏。
游戏的每一关难度都很大,因此两班的同学都只专注于某一关游戏。
现在,两班的同学都按照学号排好了队,每个班都有学号从1号到n号,共n人。你可以按照需要从两个班级中,按学号挑选n个游戏精英,也就是某个学号为x的两位同学,你可以选择任意一位。
通关游戏的规则十分苛刻,从第0关开始,必须连续通关,缺少精通某一关的高手,就会在这一关“Game Over!”。请你开动脑筋,考虑在最坏情况下,通关游戏可以进行到最小的关卡。同时,输出这种可行选择方案的方案数。
两种选择方案不同,当且仅当,存在某一个学号,一个方案选择了A班同学,另一个方案选择了B班同学。
输入
第一行一个整数n。表示每个班的人数。
第二行包含n个整数Ai,表示学号i的A班同学,擅长的关卡编号。
第三行包含n个整数Bi,表示学号i的B班同学,擅长的关卡编号。
输出
第一行包含通关游戏能到的最小关卡编号。
第二行包含能到最小关卡的选择方案数。该数值可能很大,对998244353取模。
样例输入 Copy
3
0 1 2
1 2 3
样例输出 Copy
0
4
提示
【样例解释】
学号1~3号的3个人分别选择班级为BAA、BAB、BBA、BBB这4中选法,由于没有人会玩第0关,导致这些选法在第0关就“Game Over!”了。

【数据范围】
对于20%的数据满足,N≤20;
对于50%的数据满足,N≤2000;
对于80%的数据满足,N≤100000;
对于100%的数据满足,N≤106;0≤A[i],B[i]≤109。
对于每个数据点,若答对第一问,至少获得一半的分数。

思路:考虑dp,令dp[i]:选前i个人能打的最少关卡;方案数的话假设在第dp[n] (k)关都不能选取,那么就是2^n,否则就是2 ^ (k-1);状态转移的时候只要找最小值就行,看代码应该好理解

#include<iostream>
#include<cstring>
#include<string>
#include<math.h>
#include<algorithm>
#include<vector>
#include<queue>
#include<deque>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<unordered_map>
//#pragma GCC optimize(2)
#define eps 1e-9
#define fi first
#define se second
#define pb push_back
#define db double
#define ll long long
#define PII pair<int,int >
#define cin(x) scanf("%lld",&x)
#define cout(x) printf("%lld ",x)

#define mem(a,b) memset(a,b,sizeof(a)) 
#define IOS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
#define int ll

using namespace std;

const int N = 5e6,M = 2000;
const int mod = 998244353;
const int inf = 1e18;
const double pai = acos(-1);

int T,n,m,k;
PII va[N];
int dp[N];

signed main()
{
	IOS;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>va[i].fi;
	for(int i=1;i<=n;i++) cin>>va[i].se;
	sort(va+1,va+1+n);
	
	dp[0] = 0;
	for(int i=1;i<=n;i++)
	dp[i] = min(dp[i-1]+(va[i].fi==dp[i-1]),dp[i-1]+(va[i].se==dp[i-1]));
	
	int ans = 1;
	for(int i=1;i<=n;i++)
	{
		if(va[i].fi==dp[n]||va[i].se==dp[n]) ans *= 1;
		else ans = ans*2%mod;
	}
	cout<<dp[n]<<"\n"<<ans<<"\n";
	return 0;
}

问题 E: 朋友

题目描述
经过六年的努力,小明终于被一所知名中学录取。优秀的小明总是对一些奇奇怪怪的事情感兴趣,这次他想知道谁在这所新学校拥有的朋友最多,由于大家都才刚报到,所以小明只知道大家两两之间是否是朋友关系。

输入
的第一行有两个整数n和m,n表示总人数,m表示总关系数。
接下来n行,每行有2个以空格隔开的整数a和b,表示a和b是朋友,a和b均为1到n之间的整数。不会给出重复的朋友关系。
输出
仅有一行,表示朋友数最多的人所拥有的朋友,每两个整数之间用空格隔开,按照字典序从小到大输出。如果存在多个人朋友数都是最多的情况,请输出字典序最小的那人的答案,具体见样例。
样例输入 Copy
3 3
1 2
2 3
1 3
样例输出 Copy
2 3

思路:没有思路大水题,写不对的都有问题

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
# include<bits/stdc++.h>
# include<unordered_map>
 
 
# define eps 1e-9
# define fi first
# define se second
# define ll long long
# define int ll
// cout<<fixed<<setprecision(n) 
//bool operator<(const Node& x )
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int > PII; 
const int mod=998244353;
const int N=1e6+10;
const int Time=86400;
const int X=131;
const int inf=0x3f3f3f3f;
const double PI = 1e-4;
double pai = 3.14159265358979323846; 
 
int T,n,m,k,ans,sum,tt,root,x,y;
int a[N],b[N];
vector<int>g[N];
PII p[N];
bool cmp(PII a,PII b){
      if(a.first == b.first) return a.second < b.second;
      return a.first > b.first;
}
void solve(){
         cin >> n >> m;
         for(int i = 1 ; i <= m ; i ++ ){
              cin >> x >> y;
              g[x].push_back(y);
              g[y].push_back(x);
         }
         for(int i = 1 ; i <= n ; i ++ ){
              p[i]={g[i].size(),i};
         }
         sort(p+1,p+1+n,cmp);
         sort(g[p[1].second].begin(),g[p[1].second].end());
          
         for(auto i : g[p[1].second]) cout<<i<<" ";
} 
/*
 
11
*/
signed main(){  
    std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 
    //cin >> T;
     T = 1;
    while(T--){
        solve();
    } 
    return 0; 
}

问题 F: 跳棋

题目描述
小明迷恋上了一个新的跳棋游戏,游戏规则如下:棋盘是一排从0开始,顺序编号的格子,游戏开始时你位于0号格子,你每次只能往编号大的格子跳,而且你每次至少需要跳过L个格子,至多只能跳过R个格子。每个格子都有一个给定的伤害值,显然你希望得到的伤害值越少越好。
你能告诉小明他当他跳到最后一个格子时受到的累积伤害值最小为多少吗?
如果无论如何小明都无法跳到最后一个格子,这个时候你需要输出”-1”。
注:从i号格子跳过x个格子表示从i号格子跳到第i+x+1号格子。
输入
第一行有三个整数n、L和R,n表示格子的编号从0到n。L和R表示最少需要跳过的格子数和最多能够跳过的格子数。
第二行有n个正整数,两个数字间用空格隔开,表示每个格子的伤害值。
输出
仅有一个整数,表示受到的最小伤害值,保证结果小于max long int。
样例输入 Copy
10 2 6
1 3 5 7 9 2 4 6 8 10
样例输出 Copy
12
提示
在这里插入图片描述

50%的数据,1<=n<=1000
65%的数据,1<=n<=10000
100%的数据,1<=n<=1000000,1<=L<=R<=n

其中有15%的数据,1<=n<=1000000,1<=L<=R<=10
补题补的
思路:单调队列优化dp
f[i]=min(f[i]+f[j]+a[i])(i-r-1<=j<=i-l-1)

#include<iostream>
#include<cstdio>
using namespace std;
long long n,l,r,head,tail,que[1000005],a[1000005],f[1000005];
int main()
{
 
    cin>>n>>l>>r;
    for(int i=1;i<=n;i++)
     cin>>a[i],f[i]=2147483647;
    head=1;
    for(int i=1;i<=n;i++)
    {
        if(i-l-1>=0)
        {
            while(head<=tail&&f[que[tail]]>=f[i-l-1])tail--;
            que[++tail]=i-l-1;
        }
        while(head<=tail&&que[head]<i-r-1)head++;
        if(head<=tail)f[i]=a[i]+f[que[head]];
    }
    if(f[n]>=2147483647)cout<<-1;
    else cout<<f[n];
    return 0;
}

问题G 分数统计2

题目描述
在统计完朋友情况之后,小明又对大家的毕业学校产生兴趣,但是他觉得单纯的统计人数是一件非常无聊的事情,于是他设计了一个算法,同一所学校毕业的学生,第1个将获得1分,第2个获得2分,第3个获得4分…,第i个将获得2i-1分,总分就是这所小学的得分,小明想知道得分最高的学校有多少分。
输入
第一行有两个整数n和m,n表示总人数,m表示已知的同校关系数量。
接下来n行,每行有2个以空格隔开的整数a和b,表示a和b是来自同一所学校,a和b均为1到n之间的整数。不会给出重复的信息。
输出
只有一行,为所有学校中的最高得分。最后得分可能会很大,你只需要输出后100位即可,不足100位的请直接输出。
样例输入 Copy
5 3
1 2
3 4
1 3
样例输出 Copy
15
提示
1、2、3、4来自同一所学校,该所学校所得的分数为1+2+4+8=15

60%的数据,1<=n<=10
80%的数据,1<=n<=70
100%的数据,1<=n<=10000,1<=m<=100000

思路:并查集,加上题目已经说了得分很大,所以考虑输出用高精度。

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
# include<bits/stdc++.h>
# include<unordered_map>
 
 
# define eps 1e-9
# define fi first
# define se second
# define ll long long
# define int ll
// cout<<fixed<<setprecision(n) 
//bool operator<(const Node& x )
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int > PII; 
const int mod=998244353;
const int N=1e6+10;
const int Time=86400;
const int X=131;
const int inf=0x3f3f3f3f;
const double PI = 1e-4;
double pai = 3.14159265358979323846; 
 
int T,n,m,k,ans,sum,tt,root,x,y;
int a[N],b[N],f[N],c[N];
int find(int x){
       if(x != f[x]) return f[x] = find(f[x]);
       return f[x];
}
void solve(){
          cin >> n >> m;
          for(int i = 1 ; i <= n ; i ++ ) a[i] = 1,f[i] = i;
          for(int i = 1 ; i <= m ; i ++ ){
              int u,v;
              cin >> u >> v;
              int fu = find(u) , fv = find(v);
              if(fu != fv){
                      f[fu] = fv;
                      a[fv]+=a[fu];
                }
                ans = max({ans,a[fu],a[fv]});
          }
          b[100] = 1 , c[100] = 1;
          for(int i = 2 ; i <= ans ; i ++ ){
               int v = 0;
               for(int j = 100 ; j >= 1 ; j --){
                      b[j] = b[j] * 2 + v;
                      v = b[j]/10;
                      b[j]%=10;
                 }
                 v = 0;
                 for(int j = 100 ; j >= 1 ; j --){
                      c[j] = b[j] + c[j] + v;
                      v = c[j] / 10;
                      c[j]%=10;
                 }
          }
          int i = 1;
          while(c[i] == 0 && i <= 100) i++;
          for(;i<=100;i++) cout<<c[i];
} 
/*
 
11
*/
signed main(){  
    std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 
    //cin >> T;
     T = 1;
    while(T--){
        solve();
    } 
    return 0; 
}

问题 H: 迷宫大门

题目描述
在跳棋游戏大获全胜后,小明就开始一个人在校园里溜达了。突然他在校园角落里发现了一面神奇的墙壁,墙壁上有一排钉子,每个钉子上都挂着一根两端系有小球的绳子,如下图所示
在这里插入图片描述

小明可以调整每一根绳子在钉子左右两端的长度,当来自不同绳子的相邻小球高度一样时(具体可见样例说明),便可获得积分1分。当小明的方案获得最高积分时,迷宫大门就会开启,小明就可以进去寻找宝藏啦!
输入
第一行为一个正整数n,表示墙上的绳子数。
接下来n行,每行2个整数a和b,表示绳子左右两端的初始长度。
输出
仅有一个正整数,表示小明可以获得的最高积分。
样例输入 Copy
3
1 1
3 2
1 4
样例输出 Copy
2
在这里插入图片描述
在这里插入图片描述
首先我们每次记录一组l和r
分别表示前一个位置所能接受的的最小高度和最大高度
用一个数组存和(第i根绳子最大长度)
一开始l=0,r=a[1] //a[i]表示第i个位置绳子长度的总和
2 to n
如果a[i]<minn,则无法接受,将minn更新为0,maxx更新为s[i]即可
否则累加答案并更新:
将l更新为ai与(r和a[i])最小值的差值,再-a[i]得到r
(很绕,但是自己慢慢理解能理解出来-最小)

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
# include<bits/stdc++.h>
# include<unordered_map>
 
 
# define eps 1e-9
# define fi first
# define se second
# define ll long long
# define int ll
// cout<<fixed<<setprecision(n) 
//bool operator<(const Node& x )
using namespace std;
typedef unsigned long long ull;
typedef pair<int,int > PII; 
const int mod=998244353;
const int N=1e6+10;
const int Time=86400;
const int X=131;
const int inf=0x3f3f3f3f;
const double PI = 1e-4;
double pai = 3.14159265358979323846; 
 
int T,n,m,k,ans,sum;
int a[N],b[N],f[N],c[N];
 
void solve(){
           cin >> n;
           for(int i = 1 ; i <= n ; i ++ ){
               int x,y;
               cin >> x >> y;
               a[i] = x + y;
           }
           int r = a[1],l = 0;
           for(int i = 2 ; i <= n ; i ++ ){
                 if(a[i] < l){
                          l = 0;
                          r = a[i];
                    }
                 else{
                      ans++;
                      int L = l,R = r;
                      l = a[i] - min(a[i],R);
                      r = a[i] - L;
                 }
           }
           cout<<ans;
            
} 
/*
2 5 5
r = 2 ,l = 0
r = 2, l = 3
r = 2, l = 3
11
*/
signed main(){  
    std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); 
    //cin >> T;
     T = 1;
    while(T--){
        solve();
    } 
    return 0; 
}

问题 I: 点牛

题目描述
在农夫FJ(Farmar John)的农场中有成千上万头奶牛,庞大的牛群给FJ增添了很多麻烦,尽管他为每一头奶牛从1开始顺序编上了号码,但是在每天清晨点名时(确切的说是点牛),FJ还是常常被巨大的数字弄得头晕,比如:
FJ:奶牛一亿两千三百四十五万九千九百九十九号!
Cow 123459999:到!
FJ:奶牛一亿两千三百四十……??? ……!@#KaTeX parse error: Expected 'EOF', got '#' at position 3: !@#̲
你能帮助FJ解决这个问题吗?你只需要写一个程序,当FJ点到一头奶牛时,及时提醒他下一头奶牛的编号就可以了。_

输入
仅有一个整数:即FJ刚刚点过的奶牛的编号N。1 ≤ N ≤ 1050。
输出
快告诉FJ下一头奶牛的编号吧!
样例输入 Copy
3
样例输出 Copy
4

高精度模板

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
char c[100];
int a[100],len;
void jw(int i){        
    if(i>len) len=i;
    a[i]++;
    if(a[i]==10) {
        a[i]=0; jw(i+1);
    }
    return;
}
int main(){
    scanf("%s",&c);
    len=strlen(c);
    for(int i=0;i<strlen(c);i++) a[i+1]=c[strlen(c)-i-1]-'0'; 
    jw(1);               
    for(int i=len;i>=1;i--) cout<<a[i];
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值