NKOJ 4月9日练习赛800[题解]

A.616问题(签到)

题目

子串是连续的。

果老师最喜爱的字符串是616。

果老师得到了一个纯数字的字符串S,他想知道在可以任意打乱顺序的情况下,最多有多少个不同的子串为616。

当两个子串 S1[l1...r1],S2[l2....r2] 满足 l1不等于l2且r1不等于r2或时它们被认为是不同的。

Input

11
11451419266

Output

1

思路

  • 只需要考虑1和6的个数
  • 让616最多的格式为6161616.....让前后字符串共用6,6的个数比1的个数多一。
  • 设1的个数为cnt1,6的个数为cnt6,则616个数为cnt6-1或者cnt1的个数,取最小值即可,注意没有1或6的情况。
  • 公式:max(min(cnt6-1,cnt1),0)

代码

#include<iostream>
#include<bits/stdc++.h>

using namespace std;

int main(){
    int cnt1=0, cnt6=0, n;
    string ch;
    cin >> n >> ch;
    for (int i = 0; i < n;i++){
        if(ch[i]=='1')
            cnt1++;
        else if(ch[i]=='6')
            cnt6++;
    }
    cout << max(min(cnt6-1,cnt1),0) << endl;
}

B.计数问题(数学)

题目

何老板给果老师出了一个难题:

求有多少个不同的正整数三元组 (i,j,k) 满足 根号i+根号j=根号k ,且i*j<=n。

果老师并不会做,你能略施援手吗?

当两个三元组 (i1,j1,k1),(i2,j2,k2) 满足i1不等于i2或j1不等于j2或k1不等于k2时它们被认为是不同的。

Input

1

Output

1

思路

  • 两边平方得到i+j+根号i乘j=k,所以可以知道i乘j为完全平方数
  • 我们令i*j=X^2,所以枚举小于N的所有X,在把每个X分解后计数即可
  • 注意在x为平方数时只有一种组合,其他情况有两种组合

代码

#include<iostream>
#include<cmath>

using namespace std;

int main(){
    long long n,cnt=0;
    cin >> n;
    for (int i = 1; i * i <= n; i++)
    {
        for (int j = 1; j <= i;j++){
                if(i==j)
                    cnt++;
                else if(i*i%j==0){
                    cnt+=2;
                }
        }
    }
    cout << cnt << endl;
}

C.魔法问题(DP)

题目

果老师n个元素( 编号1~n ),第 个元素的能量值为ai。

果老师可以选择至少k个元素来施放一次魔法,魔法消耗的魔力是这些元素能量值的极差。形式化地,若所用元素编号集合为S,则消耗的魔力为选择的集合中的最大值减去最小值的差。

果老师要求每个元素必须被使用恰好一次。

果老师想知道他最少需要多少魔力才能用完所有元素,请你告诉他。

Input

4 2
8 7 114514 114513

Output

2

思路

  • 先把能量值排序
  • 我们设dp[i]为前i个的最小花费即为前i个的最优情况
  • 情况一:dp[i+1]与是前一个dp[i]加上a[i]与a[i-1]的差值即为dp[i+1]=dp[i]+a[i+1]-a[i]
  • 情况二:dp[i+1]与前面断开,连接前k个再加上原数组的差即为dp[i+1]=dp[i+1-k]+a[i+1]-a[i-k+2]
  • 公式为情况一与情况二取最小值

代码

#include<bits/stdc++.h>

using namespace std;

int main(){
    long long n, a[1000005], dp[1000005], k;
    cin >> n >> k;
    for (long long i = 1; i <= n;i++)
        cin >> a[i];
    sort(a + 1, a + n + 1);
    memset(dp, 0x3f3f3f3f, sizeof(dp));
    dp[k] = a[k] - a[1];
    for (long long i = k + 1; i <= n; i++)
    {
        dp[i] = min(dp[i - 1] + (a[i] - a[i - 1]), dp[i - k] + (a[i] - a[i - k + 1]));
    }
    cout << dp[n] << endl;
}

D.通道问题(DP)

题目

果老师n个元素( 编号1~n ),第 个元素的能量值为ai。

果老师可以选择至少k个元素来施放一次魔法,魔法消耗的魔力是这些元素能量值的极差。形式化地,若所用元素编号集合为S,则消耗的魔力为选择的集合中的最大值减去最小值的差。

果老师要求每个元素必须被使用恰好一次。

果老师想知道他最少需要多少魔力才能用完所有元素,请你告诉他。

Input

2
1 2

Output

1

思路

  • 首先将权值去重(权值相等的点连接代价为0),
  • 设去重之后有𝑚个点,接下来找到最小的二进制位k,满足存在vi的这个二进制位是0且存在vj的这个二进制位是1,答案为2^k*𝑚 − 1
  • 相当于所有的这位是0的点跟𝑗连边,是1的点跟𝑖连边。

代码(出题人)

#include <bits/stdc++.h>

using namespace std;

const int N = 2e5 + 5;

int n, k;
int v[N], va, vo, ans;
int main(){
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> v[i];
    sort(v + 1, v + n + 1);
    va = 0x7fffffff;
    for (int i = 1; i <= n; i++)
    {
        va &= v[i];
        vo |= v[i];
    }
    v[n + 1] = 2e9;
    for (int i = n; i; i--)
        k += (v[i] != v[i + 1]);
    va ^= vo;
    for (int i = 0; i <= 30; i++)
    {
        int cur = 1 << i;
        if(va&cur) {
            ans = cur * (k - 1);
            break;
        }
    }
    cout << ans << endl;
    return 0;
}

一些其他的东西

如有错误请指出!

全部评论
认真听课,熟练运用,果老师肯定很开心。
点赞 回复 分享
发布于 2020-04-12 14:44

相关推荐

06-16 15:04
黑龙江大学 Java
零OFFER战士:另一个版本查看图片
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务