2022-04-06每日刷题打卡

本文探讨了两个算法问题,一是如何找到满足特定条件的最长上升子序列,二是如何计算01字符串中至少包含一个1的子串数量。对于最长上升子序列,关键在于增加一个位置倍数关系的条件;而对于字符串子串计数,重点在于优化1的分布以最大化子串数量。解决方案分别涉及动态规划和等差数列求和,展示了算法在解决实际问题中的应用。
摘要由CSDN通过智能技术生成

2022-04-06每日刷题打卡

代码源——每日一题

优美!最长上升子序列 - 题目 - Daimayuan Online Judge

多组数据。

每组将给定一个数组。派派希望从中选择一个递增的子序列,越长越好。

但派派认为,这样选出来的子序列依然不够「优美」,形式化的讲,派派希望选择的下标(从 1 开始)需要满足

i1∣i2∣i3∣⋯∣ik

其中 a|b 表示整除, 即 a 是 b 的约数。

请你帮助派派完成任务吧!

注:子序列的含义不再赘述。

输入格式

第一行一个整数 T,表示接下来有 T 组数据。

每组数据包含两行,第一行包含一个整数 N。

随后一行,包含 N 个整数,表示原数组 {A}。

输出格式

对于每组数据,输出一行,包含一个数,表示能选出的「优美」的最长上升子序列长度。

数据规模
  • 1≤T≤100
  • 1≤N≤10^6,但保证 ∑i=1TNi≤10^6
  • 1≤Ai≤10^9
样例输入
4
4
1 4 6 7
2
2 2
10
1 2 3 4 5 6 7 8 9 10
10
10 9 8 6 5 2 3 1 2 1
样例输出
3
1
4
1

解释:

对于第一组数据,能选择的「优美」最长上升子序列为 {A1,A2,A4}={1,4,7}。

对于第三组数组,选择 {A1,A2,A4,A8}={1,2,4,8}。

对于第四组数据,可选择的「优美」最长上升子序列长度为 1。

一开始有点懵,不理解是什么意思,后来发现跟一般的求最长上升子序列差不多。

这里和一般的求最长上升子序列就多了一个条件:选择的最长上升子序列,他们在数组v里的位置(从1开始,不是0)要呈倍数关系。那我从位置1开始,只看我枚举位置的倍数能不能凑成上升子序列就行。比如我枚举的位置是2,那我就看4 6 8……这些位置能不能和第二个位置凑成上升序列。我们可以准备一个dp数组,dp[i]的意思是,以v[i]为结尾的最长上升子序列的长度。我们就看,如果倍数位置j的数大于我们枚举的位置i的数,就看是当前dp[j]存的位置和dp[i]+1那个大。我们选最大的。最后输出dp数组里存储的最大值。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n';
typedef long long ll;
typedef pair<int, int> PII;
const int N = 4e5 + 50;

inline int read() {
    int x = 0; char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return x;
}

void write(int x) {
    if (x > 9) write(x / 10);
    putchar(x % 10 | '0');
}

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    t=read();
    while (t--)
    {
        int n, mx = 1;
        n=read();
        vector<int>v(n+1), dp(n+1,1);
        for (int i = 1; i <= n; i++)v[i]=read();
        for (int i = 1; i <= n; i++)
        {
            for (int j = i * 2; j <= n; j += i)
            {
                if (v[j] > v[i])dp[j] = max(dp[j], dp[i] + 1);
                mx = max(mx, dp[j]);
            }
            
        }
        write(mx);
        putchar('\n');
    }
    return 0;
}
Ayoub’s function - 题目 - Daimayuan Online Judge
题目描述

定义函数 f(s) ,s为01字符串,f(s)为字符串s中至少包含一个1的子串数量

现在存在一字符串s,给出字符串长度 n ,和它包含的1的数量 m , 求最大的可能的 f(s)

输入格式

输入由多组测试数据组成

第一行输入一个整数 1≤T≤10^5 为数据组数

接下来 T 行,每行输入两个整数 n,m (1≤n≤10^9,0≤m≤n)

输出格式

输出 T 行,每行一个整数做为答案

输入样例
5
3 1
3 2
3 3
4 0
5 2
输出样例
4
5
6
0
12
样例解释

第一组数据中,s=010时,f(s)=4

第二组数据中,s=101时,f(s)=5

第三组数据中,s=111时,f(s)=6

第四组数据中,s=0000时,f(s)=0

第五组数据中,s=01010时,f(s)=12

这题我觉得要比div2还简单些。

其实我们也能感觉到,当1分开放且尽量放在中间,能得到的f(s)最大,毕竟这样每个1能提供的贡献可以最大,比如长度为6,1个数为2,001010和000011和001100得到的f(s)分别是,16、7、15。其实也可以验证,当1分开放时,每个1可以提供的长度为2的串有01和10两种,这样两个1可以提供4个,但要是两个1挨在一起,那一共就是01、11和10三种,少了一种,放在边界也是一样的。

这里1分开的 距离越远且离边界的距离越远,他们可以单独提供的f(s)就越大。这样可以提高每个1的利用率,所以我们就把每个1等距的摆放即可。然后每个1能单独提供的子串数量取决于它们距离边界或其它1的距离,比如00100100,每个1可以单独提供2个长度为2和3个长度为3的字符串,我们先算它们能单独提供的f(s)是多少。然后再算其它1合在一起的f(s),两者加一起即可。其它1合在一起的f(s)也好算,当每个每个1能提供的长度算完后,后面的字符串长度每次都会有至少一个的1在里面,那么我们就枚举字符串的长度一直到n为止就行。当然一个个算是会超时的,但我们也能看出来,这两次计算f(s)都是一种等差数列的情况,我们可以用等差数列的前n项和来算,由于第一次的f(s)是计算每个1的单独贡献,所以是1的数量乘上每个1的贡献。

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>
#include<math.h>
#include<set>
#include<numeric>
#include<string>
#include<string.h>
#include<iterator>
#include<map>
#include<unordered_map>
#include<stack>
#include<list>
#include<queue>
#include<iomanip>

#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")

#define endl '\n';
typedef long long ll;
typedef pair<int, int> PII;
const int N = 4e5 + 50;

int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int t;
    cin >> t;
    while (t--)
    {
        ll n, m;
        cin >> n >> m;
        ll res = m;
        if (m == 0)
        {
            cout << 0 << endl;
            continue;
        }
        
        n -= m;
        ll ans = n / (m + 1) + 1;
        res += m * ((2 + ans) * (ans - 1) / 2);
        res += (1 + n - ans + m) * (n - ans + m) / 2;
        cout << res << endl;
        
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值