牛客小白月赛61

A-超市里扫货

        https://ac.nowcoder.com/acm/contest/46597/A

        题意:Flash要按照顺序购买商品,并且他的购物车有最大上限,装不下了就去卸货,问要多少次才能全部买完

        分析:水题,遍历即可

#include <bits/stdc++.h>

using namespace std ; 
const int N = 1e5 + 8 ; 
int num[N] ; 
using ll = long long ; 
int main(){
    int n , v ; 
    cin >> n >> v ; 
    for(int i = 0 ; i < n ; i ++){
        cin >> num[i] ; 
    }   
    ll ans = 0 ; 
    ll tmp = 0 ; 
    for(int i = 0 ; i < n ; i ++){
        tmp += num[i] ; 
        if(tmp > v) ans ++ , tmp = 0 , tmp += num[i] ; 
    }
    if(tmp > 0) ans ++ ; 
    cout << ans << '\n' ; 
    
    return 0 ; 
}

B-柜台结账

        https://ac.nowcoder.com/acm/contest/46597/B

        题意:Flash去买东西但是傻乎乎的不会判断“四舍五入”后的结果比原来的大还是比原来的小:

        1.当小数部分>0.5时,向上取整

        2.当小数部分<0.5时,向下取整

        3.当小数部分==0.5时,取离这个数最近的偶数

        分析:容易知道,当小数部分>0.5时,结果肯定大;小数部分<0.5时,结果肯定小;当小数部分==0.5时,判断整数部分的最后一位是奇数还是偶数即可。因为输入的是字符串,比赛时>0.5的部分想当然的处理了,结果WA了一发,痛苦

        PS:比赛当天是笔者生日,出题人是我师哥,所以这题的YXGG其实就是我哈哈哈哈哈哈哈哈哈,谢谢HR师哥 

#include <bits/stdc++.h>

using namespace std ; 

bool judge(string s){
    for(int i = 1 ; i < s.size() ; i ++){
        if(s[i] != '0') return true ; 
    }
    return false ; 
}

int main(){
    string a1 , a2 ; 
    cin >> a1 >> a2 ; 
    if(a2[0] > '5' || (a2[0] == '5' && judge(a2))) cout << "Happy birthday to MFGG\n" ; 
    else if(a2[0] < '5' && a2[0] > '0'){
       cout << "Happy birthday to YXGG" << '\n' ; 
    }
    else if(a2[0] == '0') cout << "PLMM" << '\n' ; 
    else {
        long long t = a1.size() - 1 ; 
        int tmp = a1[t] - '0' ; 
        if(tmp % 2 == 0) cout << "Happy birthday to YXGG" << '\n' ; 
        else cout << "Happy birthday to MFGG\n" ; 
    }
    return 0 ; 
}

 C-小猫觅食

        https://ac.nowcoder.com/acm/contest/46597/C

        题意:小猫咪想吃小鱼干,PLMM身上带了小鱼干,PLMM只能在曼哈顿距离<=r1的范围内活动,而小猫咪能闻到曼哈顿距离<=r2范围内的小鱼干,当小猫咪闻到小鱼干的味道的时候,PLMM就停止移动,小猫咪就直接往PLMM的方向移动,并且小猫咪并没有移动范围

        分析:对小猫咪和PLMM分别走一遍BFS即可,结果就是所有满足条件的点去最小值

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
using namespace std ; 
const int N = 1e3 + 8 ; 
char mp[N][N]; 
int n , m ; 
int r1 , r2 ; 
int dis[2][N + 8][N + 8] ; 

bool judge(int x , int y){
    if(x < 1 || x > n || y < 1 || y > m) return false ; 
    return true ; 
}

int getlen(int a , int b , int c , int d){
    return abs(a - c) + abs(b - d) ; 
}

const int dx[] = {-1 , 0 , 1 , 0} ; 
const int dy[] = {0 , 1 , 0 , -1} ; 

void bfs(int cat , int x , int y , int r){
    struct node{
        int x , y ; 
    } ; 
    queue<node> q ; 
    node fir ; 
    fir.x = x , fir.y = y , dis[cat][x][y] = 0 ; 
    q.push(fir) ; 
    while(!q.empty()){
        auto t = q.front() ; 
        q.pop() ; 
        for(int i = 0 ; i < 4 ; i ++){
            int tx = t.x + dx[i] ; 
            int ty = t.y + dy[i] ; 
            if(judge(tx , ty) && mp[tx][ty] == '.' && dis[cat][tx][ty] == inf && getlen(tx , ty , x , y) <= r){
                node tmp ; 
                tmp.x = tx , tmp.y = ty ; 
                dis[cat][tx][ty] = dis[cat][t.x][t.y] + 1 ; 
                q.push(tmp) ; 
            }
        }
    }
}

int main(){
    cin >> n >> m >> r1 >> r2 ; 
    for(int i = 1 ; i <= n ; i ++){
        for(int j = 1 ; j <= m ; j ++){
            cin >> mp[i][j] ;  
        }
    }
    int mx , my ; 
    memset(dis , 0x3f , sizeof dis) ; 
    for(int i = 1 ; i <= n ; i ++){
        for(int j = 1; j <= m ; j ++){
            if(mp[i][j] == 'P') bfs(0 , i , j , r1) ; 
            else if(mp[i][j] == 'M') bfs(1 , i , j , inf) , mx = i , my = j ; 
        }
    }
    int ans = inf ; 
    for(int i = 1 ; i <= n ; i ++){
        for(int j = 1 ; j <= m ; j ++){
            if(getlen(i , j , mx , my) <= r2){
                ans = min(ans , dis[0][i][j] + dis[1][i][j]) ; 
            }
        }
    }
    if(ans == inf) ans = -1 ; 
    cout << ans << '\n' ; 

    return 0 ; 
}

 D-石油大亨

        https://ac.nowcoder.com/acm/contest/46597/D

        题意:题目背景来源于红警,因为师哥最近成迷于红警游戏而且天天直播,给出他的B站链接The___Flash的个人空间_哔哩哔哩_bilibili

        兵营生产一个工程师要花费时间et,并且需要花费金钱ec;地图上总共有n块油田,Flash想要占领全部的油田,工程师占领第i个油田需要花费的时间为t_{i},并且占领过后每秒能带来价值为p的收益,Flash初始金钱为s。问顺序占领所有油田需要多长时间

        分析:模拟题,师哥都亲自承认这道题很恶心。我就直接看了师哥的题解代码,加上了点注释,一看就懂,注意金钱可能会爆ll

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f 
using namespace std ;
using ll = long long ;  
const int N = 1e5 + 8 ;
ll n , ec , et , p , s ; 
int t[N] ; 

void solve(){
	if(s < ec){
		cout << -1 << '\n' ; 
		return ; 
	}

	queue<ll> q ; //存放的是工程师占领下一个油田的结束时间
	ll cur_time = 0 ;   //当前时间
	ll oil_cnt = 0 ; 
	for(int i = 1 ; i <= n ; i ++){  	//枚举每个油田,每次循环都是新开了一片油田
		ll nxt_time = (i == 1 ? 0 : cur_time + et) ; // 下一次开始训练工程师的时间
		while(!q.empty() && q.front() <= nxt_time){   //如果下次训练工程师前有工程师占领了新的油田
			if(s < inf) s += oil_cnt * (q.front() - cur_time) * p ; 
			cur_time = q.front() ; 
			q.pop() ; 
			oil_cnt ++ ; 
		}
		if(cur_time < nxt_time){		//计算当前时间到下次训练工程师的时间之间增加的money
			if(s < inf) s += (nxt_time - cur_time) * oil_cnt * p ; 
			cur_time = nxt_time ; 
		}
		if(s < ec){		//如果钱不足以训练新的工程师
			while(!q.empty() && s + oil_cnt * (q.front() - cur_time) * p < ec){  //如果期间还能占领新的油田
				s += oil_cnt * (q.front() - cur_time) * p ; 
				cur_time = q.front() ; 
				q.pop() ; 
				oil_cnt ++ ; 
			}
			ll wait_time = (ec - s + (ll)oil_cnt * p - 1) / oil_cnt / p ; //使得s >= ec需要的时间,并且上取整
			s += wait_time * p * oil_cnt ; 
			cur_time += wait_time ; 
		}
		s -= ec ; 
		q.push(cur_time + et + t[i]) ; 
	}
	cout << cur_time + et + t[n] << '\n' ; 
}

int main(){
	cin >> n >> ec >> et >> p >> s ; 
	for(int i = 1 ; i <= n ; i ++){
		cin >> t[i] ; 
	}
	solve() ; 

	return 0 ;
}

E-排队

        https://ac.nowcoder.com/acm/contest/46597/E

        题意:求出一个序列全排列中逆序对的和是多少

        分析:可以注意到,一对不同的数[a_{i},a_{j}](a_{i} != a_{j}),对于整个序列的全排列贡献值为n!/2(以1 和 2 举例,一个序列中的所有权排列肯定归为两种形式:....1....2..... 和 ....2....1.....,其中只有2 1 的组合产生贡献,所以一对不同的数产生的贡献就是n!/2),答案就是求出序列中所有不同的对数乘以n!/2即可 

#include <bits/stdc++.h>

using namespace std ; 
using ll = long long ; 
const int N = 1e5 + 8 ; 
const int mod = 1e9 + 7 ; 
ll a[N] ; 
int cnt[N] ; 

int main(){
    int n ; 
    cin >> n ; 
    ll ans = 0 ; 
    int a ; 
    for(int i = 1 ; i <= n ; i ++){
        cin >> a ; 
        ans = ans + (i - ++cnt[a]) % mod ; 
        ans = ans % mod ; 
    }
    for(int i = 3 ; i <= n ; i ++){
        ans = (ans * i) % mod ; 
    }
    cout << ans << '\n' ; 
    
    return 0 ; 
}

F-选座椅

        https://ac.nowcoder.com/acm/contest/46597/F

        题意:电影院中有n个座位,所有人都坐在一起,即所有人的座位是[1,2,...,n]的连续子序列。但是同时给出三个条件a_{i},b_{i},c_{i},(共有3*m个要求)要求至少有一个人坐在a_{i},b_{i},c_{i}要求的座位中,且同时满足m个要求,所以总共是有m个条件,并且每个条件分为三个小点a_{i},b_{i},c_{i},满足其中的一个就算做满足第i个条件。问坐1个人,2个人...n个人有多少不同的座位分配方式

        分析:双指针+差分。我们可以注意到,如果区间[l,r]满足m个要求,那[l,r+1]也肯定满足m个要求。我们用ans[i]来代表坐i个人有多少不同的座位分配方式。同时定义n个vector,v[i]代表的是i这个座位出现在了哪些条件里,用cnt[i]来代表第i个条件满足了几个,用tot来代表满足了几个条件,如果++cnt[i] == 1就代表是第一次满足这个条件 , tot ++;同理,如果--cnt[i] == 0就代表第i个条件不再被满足,tot -- ;  当所选区间满足m个条件时,那么就说明[l,r]到[l,n]中的所有区间都满足条件,那么对应到ans上就是长度为r - l + 1,到 n - l + 1的长度都满足条件,这里就可以用差分来解决,所以本质上就是双指针取消寻找满足m个条件的区间,再在这个基础上将所有满足条件的区间长度+1. 因为还要求出方案总数,前面求的是满足条件的位置,求座位方式再乘以阶乘即可

#include <bits/stdc++.h>

using namespace std ; 
using ll = long long ; 
const int N = 1e5 + 8 ;
const int mod = 1e9 + 7 ;  
std::vector<int> v[N];
int n , m ; 
int tot ; 
ll cnt[N] , ans[N] ; 

void add(int x){
    for(auto i : v[x]){
        if(++cnt[i] == 1) tot ++ ; 
    }
}

void del(int x){
    for(auto i : v[x]){
        if(-- cnt[i] == 0) tot -- ; 
    }
}

int main(){
    cin >> n >> m ; 
    for(int _ = 1 ; _ <= 3 ; _ ++){
        for(int i = 1 ; i <= m ; i ++){
            int x ; 
            cin >> x ; 
            v[x].push_back(i) ; 
        }
    }
    for(int l = 1 , r = 0 ; l <= n ; l ++){
        while(r + 1 <= n && tot < m){
            add(++ r) ; 
        }
        if(tot == m) ans[r - l + 1] ++ , ans[n - l + 1 + 1] -- ; 
        del(l) ; 
    }
    ll fac = 1 ; 
    for(int i = 1 ; i <= n ; i ++){
        fac = (fac % mod * i % mod) % mod ; 
        ans[i] += ans[i - 1] ; 
        cout << (ans[i] % mod * fac % mod) % mod << " \n"[i == n] ; 
    }
    return 0 ; 
}

 特别感谢HR师哥将我的生日祝福写进比赛里,HR师哥嘎嘎强

[水平有限,欢迎指正]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值