马蹄集 oj赛(双周赛7.3-7.14)

目录

围栏木桩

大厨小码哥

最长子段和

旅费

散步

抽奖

海龟

线段树

纸带

异或和

上楼梯

上楼梯2


围栏木桩


难度:黄金·时间限制:1秒四占用内存:128 M
某农场有一个由按编号排列的 ,,根木桩构成的首尾不相连的围栏。现要在这个围栏中选取一些太桩,按照原有的编号次序排列之后,这些太桩高度成一个升序序列。所谓的升序序列就是序列中的任何一个数都不小于它之前的任何一个数。试编写程序从这个围栏中选取合适的木桩使得选出的太桩个数t最大,并求出选取出t根木桩的方案总数c。
格式
输入格式:文件中的第一行只有一个数 m ,表明随后有  个问题的描述信息。每个问题的描述信息格式为 n,h,h,h.….,hn(其中hi(i=1,2,3,...,n)表示第i根木桩的高度)。
输出格式:依次输出每个问题中t和c的解,每行输出一个问题的解。
样例 1
输入:3
复制
91019876346
3 100 70 102
6 40 37 23 89 91 12
输出:4 1
复制
2 2
3 3
备注
其中:1≤m≤5,1≤n≤20,1≤hi≤ 150

def solve():
    m = int(input())
    for _ in range(m):
        n, *heights = map(int, input().split())
        b = [1] * (n + 1)
        c = [1] * (n + 1)

        for j in range(2, n + 1):
            for r in range(j - 1, 0, -1):
                if heights[j - 1] >= heights[r - 1]:
                    if b[j] < b[r] + 1:
                        b[j] = b[r] + 1
                        c[j] = c[r]
                    elif b[j] == b[r] + 1:
                        c[j] += c[r]

        max_length = max(b)
        max_index = b.index(max_length)
        max_count = 0

        for i in range(1, n + 1):
            if b[i] == max_length and heights[i - 1] <= heights[max_index - 1]:
                max_count += c[i]

        print(max_length, max_count)


solve()

大厨小码哥


 难度:黄金。时间限制:1秒巴占用内存:128 M
大厨小码哥要做一道菜,这道菜需要烹饪数个小时,达到一定的火力值。可以选择小火烹饪一次加n 点火力值,中火烹饪加 m 点火力值,大火烹饪加k点火力值,烹饪次数不限制。这道菜总共要达到: 点火力值,不多不少,才能显现出大厨小码哥的实力。但大厨小码哥觉得这还是太简单了。所以他想考考你,这道菜有多少种不同的烹饪方式(火力烹饪的顺序不同也算不同的情况,毕竟厨艺学博大精深,先小火后大火和先大火后小火烹饪的菜品会有很大不同)?由于数据很大,请输出答案 mod 1e9 +7之后的值。
格式
输入格式:四个整数x,n,m,k。
输出格式:一个整数,表示不同的方案数;若无法烹饪则输出 impossible
样例 1
输入:5123
输出:13
复制
复制
备注
所有数据均在 longlong 范围内,0<x<1000,0<n<m<k<30

#include <bits/stdc++.h>
using namespace std;
#define mem(a) memset(a, 0, sizeof(a))
#define dbg(x) cout << #x << " = " << x << endl
#define fi(i, l, r) for (int i = l; i < r; i++)
#define cd(a) scanf("%d", &a)
typedef long long ll;

ll dp[1010] = {1, 0};
const ll mod = 1e9 + 7;

int main() {
    int x;
    int val[3];
    cin >> x;
    for (int i = 0; i < 3; i++) {
        cin >> val[i];
    }
    for (int i = 1; i <= x; i++) {
        for (int j = 0; j < 3; j++) {
            if (i - val[j] >= 0) {
                dp[i] += dp[i - val[j]];
                dp[i] %= mod;
            }
        }
    }
    if (dp[x]) {
        cout << dp[x] << endl;
    }
    else {
        puts("impossible");
    }
    return 0;
}

最长子段和


多 难度:钻石时间限制:1秒四占用内存:128 M
给出一个长度为n的序列a,选出其中连续且非空的一段使得这段和最大。
格式
输入格式:第一行是一个整数,表示序列的长度n;
第二行有 n 个整数,第i个整数表示序列的第讠个数字 ai。
输出格式:输出一行一个整数表示答案。
样例 1
输入:
2 -4 3 -1 2 -4 3
输出:4
复制
复制
备注
其中:1≤n≤2e5,-1e4≤ai≤ le4

#include <iostream>
#include <vector>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> arr(n + 1);
    int max_val = arr[0];
    for (int i = 1; i < arr.size(); i++) {
        cin >> arr[i];
        if (arr[i - 1] > 0) {
            arr[i] += arr[i - 1];
        }
        max_val = max(max_val, arr[i]);
    }
    cout << max_val << endl;
    return 0;
}

旅费


多难度:黄金·时间限制:1秒四占用内存:128M
提瓦特大陆上有个贫穷的占星术士小码哥,他要从蒙德去往璃月,两个地方相隔很远,所以要搭乘车队。但是搭乘车队需要金币,而小码哥没有太多金币,幸运的是,车队在这一路上有几个停靠点,每两个停靠点之间所需要的金币数不一样,如果能选择好的话说不定能省点钱。于是小码哥找来了每个站点之间所需的路费,请你帮他找出他完成这一旅途所需要的最少的旅费。
格式
输入格式:第一行输入一个数n,表示马车中间停靠的站点数;
接下来一个 n+1行的半矩阵,表示从蒙德开始,每个站点到接下来每个站点所需要的金币数。
输出格式:输出一行一个正整数,表示完成这一旅途所需要的最少的旅费(金币数)
样例 1
输入:1
复制
6 18
9
输出:15
复制
备注
其中:1<n<1000
任意两站间所需旅费不超过10000
样例解释:
蒙德--6-->站点1,蒙德--18-->璃月
站点1--9-->璃月

//
// Created by felix on 2024/7/10.
//
#include <bits/stdc++.h>
using namespace std;
const int N=1e3 +7;
int a[N][N],dp[N],n;
int main(){
    cin >>n;
    n++;
    for (int i=0;i<n;i++){
        for (int j =i + 1;j <=n;j++)
            cin >> a[i][j];
        dp[i]=0x3f3f3f3f;
        }
    for(int i=n-1;i>=0;i--)
        for (int j = i+1;j<=n;j++)
            dp[i] = min(dp[i],a[i][j]+ dp[j]);
    cout << dp[0];
    return 0;
}

散步


·难度:黄金◎ 时间限制:1秒 四 占用内存:128 M
小码哥今天去公园散步,公园里有一个环型的小道,因为小码哥想要刷一下步数,所以他决定在小道上走 m 步。现已知环形小道长度为”,小码哥一步走1的距离,将小道坐标化:小码哥的初始位置在1 ,顺时针分别是1~n,到n后继续走就回到了1.问小码哥有多少种走路方法,使得走 m 步过后回到原点(期间可以经过原点,只要移动的序列不同就可以看作两个方法)注:小码哥只可以顺时针或逆时针走,不可以走半步,或是原地不动;
格式
输入格式:共一行,有两个用空格隔开的整数n,m(3 ≤n≤ 30,1≤m≤30)
输出格式:共一行,有一个整数,表示符合题意的方法数。
样例 1
输入:7 9
输出:18
复制
复制
备注
其中:3≤n≤30,1≤m≤30

//
// Created by felix on 2024/7/10.
//
#include <bits/stdc++.h>
using namespace std;
const int N = 37;
int dp[N][N],n,m;
int main(){
    cin >>n >>m;
    dp[0][0]=1;
    for (int i=1;i<=m;i++)
        for (int j=0;j<n;j++)
            dp[i][j]=dp[i-1][(j+1)%n] + dp[i-1][(j-1+n)%n];
            cout << dp[m][0];
            return 0;
            }

抽奖


 难度:黄金●时间限制:1秒 四占用内存:128 M
小码哥在集市上逛街,遇见了抽奖活动,抽一次2元,但是可能会抽出 1,2,3,4四种情况,他们是等概率的。
小码哥计划抽 n 次,问亏本的概率是多少(即得到的奖金小于本金),小码哥赚了超过一半本金的概率是多少(赚到的钱是 奖金-本金 后的部分)?
格式
输入格式:输入 n 表示小码哥连抽的次数。
输出格式:第一行输出亏的概率;第二行输出赚超过一半本金的概率概率用最简分数表示,具体看样例。
样例 1
输入:2
输出:3/16
复制
复制
3/16
备注
其中:1≤n< 30

//
// Created by felix on 2024/7/10.
//
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n,dp [40][160],sum1,sum2,fenmu =  1,temp;
int gcd(int a,int b){return b ==0 ? a : gcd(b,a % b);}
signed main() {
    cin >> n;
    dp[1][1]=dp[1][2] = dp[1][3] = dp[1][4]=1;
    for(int i= 1;i <= n;i++)
        for (int j=i;j<=4 *n;j++)
            for (int k =1;k <=4;k++)
                if (j>k)
                    dp[i][j] += dp[i-1][j-k];
    for (int i = n;i < 2 * n;i++)
        sum1 += dp[n][i];
    for(int i=3*n+1;i<=4*n;i++)
        sum2 += dp[n][i];
    for(int i=1;i<= n;i++)
        fenmu *= 4;
    temp = gcd(sum1,fenmu);
    cout <<sum1 / temp <<"/"<< fenmu / temp <<endl;
    temp = gcd(sum2,fenmu);
    cout <<sum2 / temp <<"/"<<fenmu / temp;
    return 0;
}

海龟


多难度:钻石时间限制:1秒四占用内存:128 M
很多人把L060编程语言和海龟图形联系起来。在这种情况下,海龟沿着直线移动,接受命令 T(转向180度)和 F(向前移动1单元)。你会收到一份给海龟的命令清单。你必须从列表中精确地改变n 个命令(一个命令可以被改变多次)。要求出海龟在遵循修改后的所有命令后,会从起点最远可以移到多远?
格式
输入格式:第一行为命令清单;
第二行为需要改变的命令数。
输出格式:一行即为答案。
样例 1
输入:FFFTFFF
输出:6
复制
复制
备注
字符串非空且长度不大于100,1≤n<50

//
// Created by felix on 2024/7/10.
//
#include <bits/stdc++.h>
using namespace std;
#define int long long

string s;
int dp[101][51][2], n, INF = 0x3f3f3f3f;

int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);
}

signed main() {
    cin >> s >> n;
    int size = s.size();
    s = ' ' + s;

    for (int i = 0; i <= size; i++) {
        for (int j = 0; j <= n; j++) {
            dp[i][j][0] = dp[i][j][1] = -INF;
        }
    }

    dp[0][0][0] = dp[0][0][1] = 0;

    for (int i = 1; i <= size; i++) {
        for (int j = 0; j <= n; j++) {
            for (int k = 0; k <= j; k++) {
                if (s[i] == 'F') {
                    if (k % 2 == 1) {
                        dp[i][j][0] = max(dp[i][j][0], dp[i-1][j-k][0]);
                        dp[i][j][1] = max(dp[i][j][1], dp[i-1][j-k][0]);
                    } else {
                        dp[i][j][0] = max(dp[i][j][0], dp[i-1][j-k][0] + 1);
                        dp[i][j][1] = max(dp[i][j][1], dp[i-1][j-k][1] - 1);
                    }
                } else if (k % 2 == 1) {
                    dp[i][j][0] = max(dp[i][j][0], dp[i-1][j-k][0]+1);
                    dp[i][j][1] = max(dp[i][j][1], dp[i-1][j-k][1]-1);
                } else {
                    dp[i][j][0] = max(dp[i][j][0], dp[i-1][j-k][1]);
                    dp[i][j][1] = max(dp[i][j][1], dp[i-1][j-k][0]);
                }
            }
        }
    }

    cout << max(dp[size][n][0], dp[size][n][1]);
    return 0;
}

线段树


多难度:钻石·时间限制:1秒四占用内存:128M
线段树模板题。已知一个数列,你需要进行下面两种操作:将某区间每一个数加上k,求出某区间每一个数的和。
格式
输入格式:第一行包含两个整数 ,m(5<n,m < 10”),分别表示该数列数字的个数和操作的总个数;第二行包含 ”个用空格分隔的整数(0- 10”),其中第 讠个数字表示数列第 讠项的初始值;接下来 m 行每行包含 3 或 4个整数,表示一个操作,具体如下:1 xy k :将区间 |x,y 内每个数加上 k,2 xy :输出区间 [x, y内的数的和。其中:1≤x≤y≤n,0≤k≤10'
输出格式:输出包含若干行整数,即为所有操作2的结果。
样例 1
输入:5 5
复制
15 42 3
2 2 4
1232
2 3 4
1 15 1
2 1 4
输出:11
复制
8
20

//
// Created by felix on 2024/7/10.
//
#include <bits/stdc++.h>
using namespace std;
const int N=1e5 + 7;
#define int long long//有时候觉得ll麻烦,就可以用。但记得改signed main
struct NODE {
    int l, r, val, lz;//lz为懒标记
}tree[4 * N];
int a[N];
 
void build(int p,int l,int r){
    tree[p].l=l,tree[p].r=r;
    if(l==r){
        tree[p].val = a[l];
        return;
    }
    int mid =l + ((r-l)>>1);
    build(p *2,l,mid);
    build(p *2 + 1,mid + 1,r);
    tree[p].val = tree[p* 2].val + tree[p * 2+ 1].val;
}
void lazy(int p,int v) {
    int s = tree[p].l, t = tree[p].r;
    tree[p].val += (t - s + 1) * v, tree[p].lz += v;
}
    void pushdown(int p){
        lazy(2 * p,tree[p].lz);
        lazy(2 * p +1,tree[p].lz);
        tree[p].lz = 0;
}
        //带懒标记区间修改,[1,r]
//为修改区间,p为当前节点编号,c为区间节点变化值,求和非求最值
        void update(int l,int r,int c,int p){
            int s =tree[p].l,t = tree[p].r;
            if (l <=s && t <=r)
                return lazy(p,c);
            if (tree[p].lz && s != t)
            pushdown(p);
            int mid = s +((t -s)>>1);
            if (l<=mid)
            update(l,r,c,p * 2);
            if (r > mid)
                update(l,r,c,p * 2+1);
            tree[p].val =tree[p * 2].val+ tree[p * 2 + 1].val;
        }
//带懒标记区间查询(区间求和),[L,r】为修改区间,p为当前节点编号
        int query(int l,int r,int p) {
            int s = tree[p].l, t = tree[p].r;
            if (l <= s && t <= r)
                return tree[p].val;
            if (tree[p].lz)
                pushdown(p);
            int mid = s + ((t - s) >> 1), sum = 0;
            if (l <= mid)
                sum = query(l, r, p * 2);
            if (r > mid)
                sum += query(l, r, p * 2 + 1);
            return sum;
        }
signed main(){
    int n,m;
    cin>>n>>m;
    for (int i=1;i<=n;i++)
        cin >>a[i];
    build(1,1,n);
    while (m--){
        int op,x,y,k;
        cin >>op;
        if (op ==1) {
            cin >> x >> y >> k;
            update(x, y, k, 1);
        }else{
            cin >>x >>y;
            cout <<query(x,y,1)<<endl;
        }
    }
    return 0;
}

纸带


多难度:钻石心时间限制:2秒四占用内存:128 M
小码哥有一张 1xn的纸带,由n个格子组成。初始有一个点在n号格子(即左数第几个)中。假设现在这个点在(z>1)号格子,每次小码哥可以对这个点进行如下操作中的一种:减法。选择一个「1,x-1]中的正整数y,将点移动到 -y号格子中。除法。选择一个[2,]中的正整数 z,将点移动到[」号格子中。当点在1号格子中时无法移动,操作结束。
求将点从 n号格子移到1号格子的方案数,案对给定的模数取模。
两个方案不同当且仅当有一步选择的操作或选择的数不同。
格式
输入格式:一行两个数,纸带长度n和模数m。
输出格式:一行一个数,表示答案对 m 取模的结果。
样例 1
输入:3 998244353
输出:5
复制
复制
备注
2≤n≤ 4e6,le8<m< le9,m 是质数

import java.util.Scanner;

public class Main {
    static final int N = 4000005;
    static final int M = 100005;
    static final int mod = 998244353;

    static long[] f = new long[N];
    static long[] g = new long[N];

    public static void solve() {
        long sum = 0, sum2 = 0;
        f[1] = 1;
        Scanner scanner = new Scanner(System.in);
        long n = scanner.nextLong();
        long p = scanner.nextLong();
        for (int i = 1; i <= n; ++i) {
            f[i] = (f[i] + ((sum + sum2) % p + g[i]) % p) % p;
            for (int j = i; j <= n; j += i)
                g[j] = (g[j] + f[i]) % p;
            for (int j = i + 1; j <= n; j += i + 1)
                g[j] = (g[j] - f[i] + p) % p;
            sum = (sum + f[i]) % p;
            sum2 = (sum2 + g[i]) % p;
        }
        System.out.println(f[(int) n]);
    }

    public static void main(String[] args) {
        solve();
    }
}

异或和


多难度:钻石时间限制:1秒巴:占用内存:128 M
给一个长度为n的序列 a1,2,….,a,寻找在a的所有递增子序列(可以为空)的异或和中出现的数。
格式
输入格式:第一行一个正整数 n 表示序列长度;第二行 几 个整数表示序列 a。
输出格式:第一行输出满足要求的数的个数;第二行从小到大输出在 a的所有递增子序列(可以为空)的异或和中出现的数。
样例 1
输入:4
复制
4 2 2 4
输出:4
复制
样例 2
0 2 46
输入:2
复制
1 5
输出:4
复制
样例 3
0 1 4 5

#include <bits/stdc++.h>

using namespace std;

const int N = 550;
const int inf = 0x3f3f3f3f;
int f[N];

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    memset(f, 0x3f, sizeof f);
    f[0] = 0;

    int n;
    cin >> n;
    for (int i = 1, x; i <= n; ++i) {
        cin >> x;
        for (int j = 0; j < N; ++j) {
            if (f[j] < x)
                f[j ^ x] = min(f[j ^ x], x);
        }
    }

    vector<int> res;
    for (int i = 0; i < N; ++i) {
        if (f[i] != inf)
            res.push_back(i);
    }

    cout << res.size() << endl;
    for (auto x : res)
        cout << x << " ";

    return 0;
}

上楼梯


难度:黄金·时间限制:1秒 四占用内存:128 M
小码哥是个调皮的孩子,他上楼梯喜欢蹦蹦跳跳,也就是说,每次他会随机向上走1级或2级台阶。无聊的你想知道他从地面上到n 层共有多少种走法。
每层楼之间有 m 级台阶。
格式
输入格式:输入两个正整数 m,n,代表每层台阶数以及要去到的楼层。
输出格式:输出一个数,代表所有可能的走法的总个数。
样例 1
输入:14
输出:3
复制
复制
备注
上楼哦!n*m< 90

#include<bits/stdc++.h> 

using namespace std;
typedef long long ll;

ll dp[100005]={0};

int main( )
{
    int n,m;
    cin>>m>>n;
    dp[0] = 0;
    dp[1] = 1,dp[2] = 2;
    for(int i=3;i<=m*(n-1);i++) dp[i] = dp[i-1]+dp[i-2];
    cout<<dp[m*(n-1)]<<'\n';
    return 0;
}

上楼梯2


少 难度:黄金时间限制:1秒巴占用内存:128 M
小码哥是个调皮的孩子,他上楼梯喜欢蹦蹦跳跳,也就是说,每次他会随机向上走1级或2级台阶。他初始在第1阶台阶上。这道题已经做过了,现在把它升级,假设这个小码哥不是个普通的小孩,他是超人宝宝,每次最多可以向上走 k 级台阶(超人宝宝当然不傻,最少也会向上走一个台阶),请你求出他从底部上到第,级台阶共有多少种可能的走法。
格式
输入格式:输入一行用空格隔开的两个正整数 n 和 k。
输出格式:输出一个数,代表所有可能的走法的总个数。由于这个数可能很大,请你输出结果对114584取模的结果
样例 1
输入:3 4
输出:4
复制
复制
备注
其中:n< 10',k< 100

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 7;
int dp[N], n, k;

int main() {
    cin >> n >> k;
    dp[0] = dp[1] = 1;
    for (int i = 2; i <= n; i++) {
        for (int j = 1; j <= min(i, k); j++) {
            dp[i] = (dp[i] + dp[i - j]) % 114584;
        }
    }
    cout << dp[n];
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值