NewOJ Week 10题解

比赛链接:http://oj.ecustacm.cn/contest.php?cid=1026

题目总览

题目TAG难度补题链接
石子堆思维题http://oj.ecustacm.cn/problem.php?id=1836
考试二进制枚举☆☆http://oj.ecustacm.cn/problem.php?id=1837
袋鼠聚会枚举☆☆http://oj.ecustacm.cn/problem.php?id=1838
单峰数组离散化、树状数组、贪心☆☆☆☆http://oj.ecustacm.cn/problem.php?id=1839
彩虹数数位 D P DP DP☆☆☆☆http://oj.ecustacm.cn/problem.php?id=1840

A 石子堆

题意:

在这里插入图片描述

Tag: 思维题

难度:

思路: 每次操作要么拿走石子,要么挪动石子,可以进行任意次。

这说明操作后的 B B B数组的石子总数,只要不超过 A A A的总数即可。

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int n;
        cin >> n;
        int suma = 0, x, sumb = 0;
        for(int _ = 1; _ <= n; _++)
            cin >> x, suma += x;
        for(int _ = 1; _ <= n; _++)
            cin >> x, sumb += x;
        if(suma >= sumb)puts("Yes");
        else puts("No");
    }
    return 0;
}

B 考试

题意:

在这里插入图片描述

Tag: 二进制枚举

难度: ☆☆

思路: 二进制枚举答案的所有情况,每道题目有两种可能 T T T或者 F F F,总共存在 2 k 2^{k} 2k种情况。对于每种情况暴力求一下最低分,再让最低分最高即可。

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

int main()
{
    int n, k, res = 0;
    cin >> n >> k;
    vector<string>s(n);
    for(auto &x : s)cin >> x;
    for(int i = 0; i < (1 << k); i++)//二进制枚举所有答案的情况
    {
        //对于每种情况,求解最低分
        int cur_min = k;
        for(auto x : s)
        {
            int cur_score = 0;
            for(int j = 0; j < k; j++)
                if((x[j] == 'F') ^ (1 & (i >> j))) //1 ^ 0 || 0 ^ 1
                    cur_score++;
            cur_min = min(cur_min, cur_score);
        }
        res = max(res, cur_min);
    }
    cout<<res<<endl;
    return 0;
}

C 袋鼠聚会

题意:

在这里插入图片描述

Tag: 枚举

难度: ☆☆

思路: 直接按照题意枚举模拟即可。枚举第一个、第二个袋鼠家,然后计算一下答案即可。

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

int main()
{
    int n;
    vector<int> houses;
    int cost, best_cost = -1;
    cin >> n;
    // read kangaroo info
    houses = vector<int>(n);
    for (int i = 0; i < n; i++)
        cin >> houses[i];
    // try all possible pairs of host kangaroos
    for (int i = 0; i < n; i++)     //first host
    {
        for (int j = i; j < n; j++)     //second host
        {
            //compute cost for these hosts
            cost = 0;
            for (int k = 0; k < n; k++)
            {
                if (abs(houses[k] - houses[i]) < abs(houses[k] - houses[j]))
                    cost += pow(houses[k] - houses[i], 2);
                else
                    cost += pow(houses[k] - houses[j], 2);
            }
            // if energy cost for this host pair is better than current best,
            // save this as new solution
            if (cost < best_cost || best_cost == -1)
                best_cost = cost;
        }
    }
    // print lowest possible energy cost
    cout << best_cost << endl;
}

D 单峰数组

题意:

在这里插入图片描述

Tag: 离散化、树状数组、贪心

思路: 由于题目对数字具体大小是无关的,只和数字间的相对关系有关,因此可以首先对数组 a a a进行离散化。

此时 a a a是一个 1 − n 1-n 1n的排列,要把 a a a变成题目给定的单峰数组——先递增、后递减。这说明对于最小的数字来说,要么在开头,要么在结尾。

这样我们可以从小到大遍历 1 − n 1-n 1n,对于每个当前的数字 i i i来说,要么将其放在递增的尾部,要么放在递减的开头,每次贪心地选择一个更优的。

每次选择如何计算贡献?假设数字 i i i对应的下标为 i d x idx idx

如果放在递增的尾部,则交换次数= [ 1 , i d x − 1 ] [1,idx-1] [1,idx1]中比数字 i i i大的数字数量,记为 b e f o r e before before(因为数字比 i i i更小的已经排列好了),利用树状数组动态维护即可。

如果放在递减的开头,则交换次数= [ i d x + 1 , n ] [idx+1,n] [idx+1,n]中比数字 i i i大的数字数量,总共有 n − i n-i ni个数字大于 i i i,前面大的数字由 b e f o r e before before个,后面还剩下 n − i − b e f o r e n-i-before nibefore个。

#include<bits/stdc++.h>
using namespace std;
const int maxn = 300000 + 10;
int n, a[maxn], b[maxn], tree[maxn], pos[maxn];
inline int lowbit(int x)
{
    return x & (-x);
}

void update(int x, int d)
{
    while(x <= n)
        tree[x] += d, x += lowbit(x);
}

int query(int x)
{
    int res = 0;
    while(x)
        res += tree[x], x -= lowbit(x);
    return res;
}

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> a[i], b[i] = a[i];
    sort(b + 1, b + 1 + n);
    for(int i = 1; i <= n; i++)
    {
        //离散化
        a[i] = lower_bound(b + 1, b + 1 + n, a[i]) - b;
        //记录每个数字的idx
        pos[a[i]] = i;
        //初始化树状数组:树状数组的下标是这里的idx
        update(i, 1);
    }
    long long ans = 0;
    for(int i = 1; i <= n; i++)
    {
        int idx = pos[i];
        int before = query(idx - 1);    //利用树状数组求before
        int after = n - i - before;
        ans += min(before, after);      //贪心选择最优的
        update(idx, -1);                //更新树状数组
    }
    cout<<ans<<endl;
    return 0;
}

E 彩虹数

题意:

在这里插入图片描述

Tag: 数位 D P DP DP

难度: ☆☆☆☆

思路: 数位DP经典做法,求区间 [ L , R ] [L,R] [L,R]中彩虹数数量相当于 [ 1 , R ] [1,R] [1,R]的数量减去 [ 1 , L − 1 ] [1,L-1] [1,L1]的数量。

利用记忆化搜索的数位 D P DP DP求解 [ 1 , n ] [1,n] [1,n]中彩虹数数量。

需要维护:当前第几位、上一位数字、前导 0 0 0、是否达到上限。

对于彩虹数只需要约束当前枚举的数字不等于上一位数字即可。注意当前面都是前导 0 0 0时这个条件可以不满足。

注意,代码中直接用 [ 1 , R ] [1,R] [1,R]的答案减去 [ 1 , L ] [1,L] [1,L]的答案,所以需要特判一下左端点是否为彩虹数。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MOD = 998244353;

string L, R;
int dp[100001][10][2][2];
//n:当前第几位、lastdig:上一位、nonzero:没有前导0、limit:达到上限
ll dfs(int n, int lastdig, bool nonzero, bool limit, string& s)
{
    if(n == s.size())return 1;
    int &res = dp[n][lastdig][nonzero][limit];
    if(res)return res;
    int up = limit ? s[n] - '0' : 9;
    for(int i = 0; i <= up; i++)
    {
        if (i == lastdig && nonzero) continue;  //彩虹数限制
        res = (res + dfs(n + 1, i, nonzero || (i != 0), limit && i == up, s)) % MOD;
    }
    return res;
}

int main()
{
    cin >> L >> R;
    ll ans_L = dfs(0, 0, 0, 1, L);
    memset(dp, 0, sizeof(dp));
    ll ans_R = dfs(0, 0, 0, 1, R);
    ll ans = (ans_R + MOD - ans_L) % MOD;
    bool L_ok = true;
    for(int i = 1, last = L[0]; i < L.size() && L_ok; last = L[i++])
        if(L[i] == last)L_ok = false;
    cout<<ans + L_ok<<endl;
    return 0;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

傅志凌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值