牛客周赛 41 A~E C++

A.小红接雨水

题意:给你三个数,代表三块木板的长度,如果形成‘凹’问可以接多少水。

思路:就是看中间那个是否是最矮的,不是的话就接不了水。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define x first
#define y second
typedef map<int, int> mp;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
#define F(i, l, r) for (int i = l; i <= r; i++)
#define endl '\n'
#define inf 0x3f
// const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
#define int long long
const int N = 2e5 + 10, INF = 0x3f3f3f3f3f3f3f3f, MOD = 1e9 + 7;

void solve()
{
    int a, b, c;
    cin >> a >> b >> c;
    if (b >= a || b >= c)
        cout << 0 << endl;
    else
        cout << min(a, c) - b << endl;
}

// int main()
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
}

B.小红的排列构造

题意:给你一个长度为n的排列和一个数k,你需要构造一个排列,使得你的排列和原来的排列恰好有k个位置的数不一样。

思路:有k个地方不一样等价于有 n-k 个位置需要一样,可以先记录原排列每个数的位置,然后先让 1~n-k 的数按照原排列排好,然后让剩下的都不一样,可以让剩下的数都放此数加一的数,然后在数n的位置放数n-k+1就好了。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define x first
#define y second
typedef map<int, int> mp;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
#define F(i, l, r) for (int i = l; i <= r; i++)
#define endl '\n'
#define inf 0x3f
// const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
#define int long long
const int N = 2e5 + 10, INF = 0x3f3f3f3f3f3f3f3f, MOD = 1e9 + 7;
int ans[N];
int pos[N];
void solve()
{
    int n, k;
    cin >> n >> k;
    vl a(n + 1);

    for (int i = 1; i <= n; i++)
        cin >> a[i], pos[a[i]] = i;// 记录每个数的位置

    if (k == 1)// 如果k等于1,做不到只让一个不一样
        cout << -1 << endl;
    else if (k > n)// 如果k>n,很明显也做不到
        cout << -1 << endl;
    else
    {
        int u = n - k;
        // 先把n-k放好
        for (int i = 1; i <= u; i++)
            ans[pos[i]] = i;

        // 放剩下的数,让原排列的这些数的位置都放数+1,除了最后一个
        for (int i = u + 1; i <= n; i++)
            if (i != n)
                ans[pos[i]] = i + 1;
            else
                ans[pos[i]] = u + 1;
       
        for (int i = 1; i <= n; i++)
            cout << ans[i] << " ";
    }
}

// int main()
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
}

C.小红的循环移动

题意:给你一个数字串,每次操作可以将数字串最高位的一个数字移到数字串最后,问最少操作几次可以使得该数字串变成4的倍数。

思路:

        我刚开始看见1e5长度的字符串以为是高精度,但是如果是高精度复杂度肯定是超的,所以这题首先排除高精度。

        然后想4的倍数有什么特点,因为4的25倍是100,所以一旦数字串超过3位数,是否是4的倍数其实就跟百位以及更高位没有关系了,所以只需要看每个数字串的个位和十位的数字组成的数字是否是4的倍数就好了。

        是四的倍数的两位数有个特点,如果十位是偶数,那个位只能是4或8;如果是奇数,个位只能是2、6、0。

        操作的移动其实不改变数字串的顺序,所以只需要倍长数字串,从前往后每次看两位数即可。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define x first
#define y second
typedef map<int, int> mp;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
#define F(i, l, r) for (int i = l; i <= r; i++)
#define endl '\n'
#define inf 0x3f
// const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
#define int long long
const int N = 2e5 + 10, INF = 0x3f3f3f3f3f3f3f3f, MOD = 1e9 + 7;

void solve()
{
    string g;
    cin >> g;
    
    int n = g.size();
    if (n == 1)
    {
        if ((g[0] - '0') % 4 == 0)
            cout << 0 << endl;
        else
            cout << -1 << endl;
        return;
    }
    g = g + g;// 直接倍长
    for (int i = n - 2, j = 0; i <= 2 * n - 3; i++, j++)// 从长度减二开始遍历
    {
        // 其实可以直接取出来两位数直接看是否是4的倍数就可以了,不用分十位的奇偶
        if ((g[i] - '0') % 2 == 1)
        {
            int res = (g[i] - '0') * 10 + (g[i + 1] - '0');
            if (res % 4 == 0)
            {
                cout << j << endl;
                return;
            }
        }
        else
        {
            if ((g[i + 1] - '0') % 4 == 0)
            {
                cout << j << endl;
                return;
            }
        }
    }
    cout << -1 << endl;
}

// int main()
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
}

D.小红的好串

题意:给你一个仅由小写字母组成的字符串,然后给你q个区间询问,问将区间对应子串改为“好串”的最小修改次数(修改可以任意修改)。

“好串”的定义为该字符串在长度和它相等的字符串中,"red"子序列的数量是最多的。

思路:首先明白什么是好串,观察字符串'rreedd'的'red'子序列的个数,为8个(2 * 2 * 2),所以要想子序列数量最多,就需要三个字母的数量尽可能接近,这样乘起来的积最大,所以可以建一棵线段树维护区间内的 'r' 'e' 'd' 三个字母的数量,然后对于每次询问,先确定最优方案,然后查最优方案当前满足了几个就可以了。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define x first
#define y second
typedef map<int, int> mp;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
#define F(i, l, r) for (int i = l; i <= r; i++)
#define endl '\n'
#define inf 0x3f
// const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
#define int long long
const int N = 2e5 + 10, INF = 0x3f3f3f3f3f3f3f3f, MOD = 1e9 + 7;

string g;
int n, q;

struct Node
{
    int l, r;
    int rr, e, d;
}tr[N << 2];

struct Q
{
    int r, e, d;
};

void pushup(Node &u, Node &l, Node &r)
{
    u.rr = l.rr + r.rr;
    u.e = l.e + r.e;
    u.d = l.d + r.d;
}

void pushup(int u)
{
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

void build(int u, int l, int r)
{
    if (l == r)
    {
        tr[u] = {l, r};
        if (g[l] == 'r')
            tr[u].rr = 1;
        else if (g[l] == 'e')
            tr[u].e++;
        else if (g[l] == 'd')
            tr[u].d++;
    }
    else
    {
        tr[u] = {l, r};
        int mid = l + r >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

Q query(int u, int l, int r)
{
    if (tr[u].l >= l && tr[u].r <= r)
    {
        Q res = {tr[u].rr, tr[u].e, tr[u].d};
        return res;
    }
    else
    {
        int mid = tr[u].l + tr[u].r >> 1;
        Q res = {0, 0, 0};
        if (l <= mid)
            res = query(u << 1, l, r);
        if (r > mid)
        {
            auto t = query(u << 1 | 1, l, r);
            res.r += t.r;
            res.e += t.e;
            res.d += t.d;
        }
        return res;
    }
}

void solve()
{
    cin >> n >> q;
    cin >> g;
    g = " " + g;
    build(1, 1, n);

    while (q--)
    {
        int l, r;
        cin >> l >> r;
        int m = r - l + 1;

        // 如果区间长度小于3,显然不存在red,这个特判昨天没注意WA了一发
        if (m < 3)
            cout << 0 << endl;
        else if (m % 3 == 0)
        {
            int j = m / 3;
            int rr = query(1, l, l + j - 1).r;
            int ee = query(1, l + j, l + 2 * j - 1).e;
            int dd = query(1, l + 2 * j, r).d;
            cout << m - (rr + ee + dd) << endl;
        }
        else if (m % 3 == 1)
        {
            int res = 1e9;
            int j = m / 3;

            // 多了一个,那么分以下三种情况
            // r + 1
            int rr = query(1, l, l + j).r;
            int ee = query(1, l + j + 1, l + 2 * j).e;
            int dd = query(1, l + 2 * j + 1, r).d;
            res = min(m - (rr + ee + dd), res);
            // e + 1
            rr = query(1, l, l + j - 1).r;
            ee = query(1, l + j, l + 2 * j).e;
            dd = query(1, l + 2 * j + 1, r).d;
            res = min(m - (rr + ee + dd), res);
            // d + 1
            rr = query(1, l, l + j - 1).r;
            ee = query(1, l + j, l + 2 * j - 1).e;
            dd = query(1, l + 2 * j, r).d;
            res = min(m - (rr + ee + dd), res);

            cout << res << endl;
        }
        else if (m % 3 == 2)
        {
            int res = 1e9;
            int j = m / 3;
            
            // 多了两个,那么分以下三种情况
            // r + 1, e + 1
            int rr = query(1, l, l + j).r;
            int ee = query(1, l + j + 1, l + 2 * j + 1).e;
            int dd = query(1, l + 2 * j + 2, r).d;
            res = min(m - (rr + ee + dd), res);
            // e + 1, d + 1
            rr = query(1, l, l + j - 1).r;
            ee = query(1, l + j, l + 2 * j).e;
            dd = query(1, l + 2 * j + 1, r).d;
            res = min(m - (rr + ee + dd), res);
            // d + 1, r + 1
            rr = query(1, l, l + j).r;
            ee = query(1, l + j + 1, l + 2 * j).e;
            dd = query(1, l + 2 * j + 1, r).d;
            res = min(m - (rr + ee + dd), res);

            cout << res << endl;
        }
    }
}

// int main()
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
}

E.小红的树上赋值(easy)

题意:给你一颗树,然后树上的一些点被染成红色,需要你给这棵树赋值,使得所有以染红的节点为根节点的子树的权值之和为0,然后要求你的染色方案在所有可行方案中绝对值之和最大。因为是简单版本,赋值的选项只有1、-1、0。

思路:

        因为以红节点为根的子树权值之和为零,所以在算一棵红节点为根的子树的权值之和的时候可以不算它的所有红节点的子树,那么赋值问题就可以转变成,为这棵树上的所有红节点为根的子树染色,即分开考虑。

        对于一棵红色根节点的树来说,上述分开考虑会使得这棵树上除了根节点其他全是未染色的节点,要让这些节点的权值之和为零,很显然关系到这棵树上的节点数量,分奇偶得染色即可。

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define x first
#define y second
typedef map<int, int> mp;
typedef vector<int> vi;
typedef vector<ll> vl;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<double, double> pdd;
#define F(i, l, r) for (int i = l; i <= r; i++)
#define endl '\n'
#define inf 0x3f
// const int N = 2e5 + 10, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
#define int long long
const int N = 2e5 + 10, INF = 0x3f3f3f3f3f3f3f3f, MOD = 1e9 + 7;

int col[N];
bool rs[N]; // 1表示红色
vector<int> root;
vector<int> k[N];
vector<int> shu[N];
int yy;
bool st[N];

int dfs(int u, int fa)
{
    shu[yy].push_back(u);// 因为要染色,所以先把当前红根树上的所有点记录下来一起染色。
    int res = 1;

    for (auto v : k[u])
    {
        if (st[v])
            continue;
        if (rs[v]) // 跳过红子树
            continue;
        st[v] = 1;
        res += dfs(v, u);
    }

    return res;
}

void dd(int u, int fa)
{
    st[u] = 1;
    col[u] = 1;

    for (auto v : k[u])
    {
        if (st[v])
            continue;
        if (v == fa)
            continue;
        if (rs[v])
            continue;
        dd(v, u);
    }
}

void solve()
{
    int n, l, r;
    cin >> n >> l >> r;
    string g;
    cin >> g;
    g = " " + g;

    for (int i = 1; i <= n; i++)
        if (g[i] == 'R')
            rs[i] = 1, root.push_back(i);

    for (int i = 0; i < n - 1; i++)
    {
        int u, v;
        cin >> u >> v;
        k[u].push_back(v);
        k[v].push_back(u);
    }
    
    // 这是这题比较坑的一点,就是1是全树的根,如果它不是红色,显然它以及它所有非染色儿子都不会影响到任何红根树,所以为了最后权值的绝对值最大,这些点最好取到最大绝对值,当然这题就是1或者-1。
    if (rs[1] == 0)
        dd(1, -1);

    for (int i = 0; i < root.size(); i++)
    {
        yy = root[i];
        st[yy] = 1;
        int cnt = dfs(root[i], -1);
        if (cnt % 2 == 0)
        {
            int kj = 1;
            for (int j = 0; j < shu[yy].size(); j++)
                col[shu[yy][j]] = kj, kj = -1 * kj;
        }
        else
        {
            int kj = 1;
            for (int j = 0; j < shu[yy].size() - 1; j++)
                col[shu[yy][j]] = kj, kj = -1 * kj;
            col[shu[yy][shu[yy].size() - 1]] = 0;
        }
    }

    for (int i = 1; i <= n; i++)
        cout << col[i] << " ";
}

// int main()
signed main()
{
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int t = 1;
    // cin >> t;
    while (t--)
    {
        solve();
    }
}

  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值