【ACM专题训练】二分法

气球消消乐

蒜头君有 n 只气球,蒜头君把气球排成一排。初始时,气球都是白色,现在蒜头君想用 m 种颜色给气球涂色,如果相邻的气球的颜色相同,这 2 个气球会发生消消乐,蒜头君希望你求出会发生消消乐的涂色方法有多少种。最后答案对 10^{12}+7 取模

输入格式

输入两个整数 n (1≤n≤12),m (1≤m≤12)

输出格式

输出一个整数表示答案。

样例输入1

2 2

样例输出1

2

样例输入2

3 4

样例输出2

28

思路:发生消除的事件数等于所有事件数(m^n),减去不发生消消乐的次数(m*((m-1)^(n-1))),即第一个气球可以涂m种颜色,剩下的气球只能涂m -1种颜色(要与前一个气球不同),采用快速幂和快速乘进行计算

mul_mod : 计算 a * b % mod的值

pow_mod :计算 a ^ b % mod的值

#include <cstdio>
#include <iostream>
using namespace std;
const long long mod = 1e12 + 7;
long long mul_mod(long long a,long long b){
    long long res = 0;
    while(b > 0){
        if(b & 1){
            res = (res + a) % mod;
        }
        a = (a + a) % mod;
        b >>= 1;
    }
    return res;
}
long long pow_mod(long long a,long long b){
    long long res = 1;
    while(b > 0){
        if(b & 1){
            res = mul_mod(res,a);
        }
        a = mul_mod(a,a);
        b >>= 1;
    }
    return res;
}
int main(){
    long long n,m;
    cin >> n >> m;
    cout << (pow_mod(m,n) - mul_mod(pow_mod(m-1,n-1),m)+mod) % mod;
	return 0;
}

跳石头

一年一度“跳石头”比赛又要开始了!

这项比赛将在一条笔直的河道中进行,河道中分布着一些巨大岩石。组委会已经选择好了两块岩石作为比赛起点和终点。在起点和终点之间,有 N 块岩石(不含起点和终点的岩石)。在比赛过程中,选手们将从起点出发,每一步跳向相邻的岩石,直至到达终点。

为了提高比赛难度,组委会计划移走一些岩石,使得选手们在比赛过程中的最短跳跃距离尽可能长。由于预算限制,组委会至多从起点和终点之间移走 M 块岩石(不能移走起点和终点的岩石)。

输入格式

输入第一行包含三个整数 L,N,M,分别表示起点到终点的距离,起点和终点之间的岩石数,以及组委会至多移走的岩石数。

接下来 N 行,每行一个整数,第 ii 行的整数 Di(0<Di<L) 表示第 i 块岩石与 起点的距离。这些岩石按与起点距离从小到大的顺序给出,且不会有两个岩石出现在同一个位置。

输出格式

输出只包含一个整数,即最短跳跃距离的最大值。

数据范围

对于 2020% 的数据,0≤M≤N≤10。

对于 5050% 的数据,0≤M≤N≤100。

对于 100100% 的数据,0≤M≤N≤50,000,

样例说明

将与起点距离为 2 和 14 的两个岩石移走后,最短的跳跃距离为 4(从与起点距离 17 的岩石跳到距离 21 的岩石,或者从距离 21 的岩石跳到终点)。

样例输入复制

25 5 2
2
11
14
17
21

样例输出复制

4

思路:二分答案,检查答案是否可行,关键在Check函数

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int a[100001],l,m,n,i;
//搜索答案,类似贪心节约用电
//最短的最长,最大的最小:考虑二分答案
bool check(int k){
    int cnt = 0,last = 0;
    for(int i = 0;i < m;i++){
    	if(a[i] - last >= k){ //注意是大于等于
            last = a[i];
        }
        else{
            cnt++;
        }
    }
    if(cnt > n){
        return false;
    }
    return true;
}
int main(){
    cin >> l >> m >> n;
    for(i = 0;i < m;i++){
        cin >> a[i];
    }
    int left = 0,right = l,mid,ans = 0;
    a[i] = l;
    while(left <= right){
        mid = (left + right) / 2;
        if(check(mid)){
            left = mid + 1;
            ans = mid;
        }
        else{
            right = mid - 1;
        }
    	//cout << mid << endl;

    }
    cout << ans;
    return 0;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值