Codeforces Round #849 (Div. 4) D F G2题解

D. Distinct Split

题意:给你一串字符,让你一分为二,问你分开后两串字符之间分别的不同字符数量之和是多少。

思路:我们可以先将所有的字符分给右边一串,然后从第一个字符开始慢慢分给左边,直到右边只剩一个停止,然后中间一直取最大值就好了。

#include<math.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<map>
#include<list>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<limits>
#define re register
#define iosgo() std::ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
#define run(i,n) for (int i = 1; i <= n; i++)
#define cin std::cin
#define cout std::cout
#define ll long long
#define endl "\n"
using namespace std;
typedef pair<char, int>pll;
pll c[200005];
int h[150][150];
ll x[200005], y[200005], z[200005];
ll gcd(ll a, ll b)
{
    return b ? gcd(b, a % b) : a;
}
int main()
{
    iosgo();
    int t; cin >> t;
    while (t--)
    {
        int g[27],l[27]; 
        memset(g, 0, sizeof(g));
        memset(l, 0, sizeof(l));
        int a; cin >> a;
        string k;
        cin >> k; int cnt = 0;
        for (int i = 0; i < a; i++)g[k[i] - 'a']++;
        for (int i = 0; i < 26; i++)
        {
            if (g[i] > 0)cnt++;
        }int ans = 0;
        for (int i = 0; i < a-1; i++)
        {
            int q = k[i] - 'a';
            l[q]++;
            g[q]--;
            if (l[q] == 1)cnt++;
            if (!g[q])cnt--;
            ans = max(ans, cnt);
        }
        cout << ans << endl;
    }
    return 0;
}

F. Range Update Point Query

题意:给你一个大小为n的数组。数组中数据最大1e9,然后让你执行q次操作。操作分为两种,第一种:改变自L-R之间的所有数一次,变成这个数本身所有位置上的数字和,比如123改变一次->6。

第二种:输出数组x位置上的数。

思路:当明白题意之后,我们肯定明白不能够遍历,那用什么办法呢?由于数据最大是1e9,那么数字最大也就是99999999->72->9。最多变两次,那么变两次这个数就不要动了。我们可以存那些可以变动的数字的下标,进行简化操作,详情见代码:

#include<math.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<map>
#include<list>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<limits>
#define re register
#define iosgo() std::ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
#define run(i,n) for (int i = 1; i <= n; i++)
#define cin std::cin
#define cout std::cout
#define ll long long
#define endl "\n"
using namespace std;
typedef pair<char, int>pll;
pll c[200005];
int h[150][150];
int  x[200005], y[200005];
ll gcd(ll a, ll b)
{
    return b ? gcd(b, a % b) : a;
}
int giao(int f)
{
    int summ = 0;
    while (f)
    {
        int k;
        k = f % 10;
        summ += k;
        f /= 10;
    }
    return summ;
}
signed main()
{
    set<int>k;
    iosgo();
    int t;
    cin >> t;
    while (t--)
    {
        k.clear();
        int  n, q;
        cin >> n >> q;
        run(i, n)
        {
            cin >> x[i];
            if (giao(x[i]) != x[i])
            {
                k.insert(i);//存储下标。
            }
        }
        k.insert(n + 1);
        run(i, q)
        {
            int a, b, c;
            cin >> a;
            if (a == 1)//分开操作
            {
                cin >> b >> c;
                int f = b;
                while (1)
                {
                    auto it = k.lower_bound(f);//利用下标二分查找是否有能够改变的数据
                    int pos = *it; 
                    if (pos > c)break;//如果下标大于C,那么代表没有可以更改的,跳过。
                    x[pos] = giao(x[pos]);//数据改变
                    if (x[pos] == giao(x[pos]))
                    {
                        k.erase(pos);//当数据最简时,把这个下标消除。
                    }
                    f= pos + 1;//切记不是f++,否则会出现有数据会被多次消除。
                }
             
            }
            else
            {
                cin >> b;
                cout  << x[b] << endl;;
            }

        }
    }
}

G2. Teleporters (Hard Version)

题意:你一开始在位置0,现在有n个传送门,每一个传送门使用需要它位置上这么多的金币,且至多使用一次。每一次使用后你可以决定传送到位置0或者n+1。在移动过程中,你每移动距离1就会消耗1金币。题目给出你的金币量,问你至多可以使用多少个传送门。

思路:由于位置的不确定,每一个传送门对应的都有两个金币使用值,所以我们需要遍历,以每一个位置为初始使用传送门进行遍历演算,在这里可以使用前缀和寻找最大解。详情见注释。

#include<math.h>
#include<algorithm>
#include<iostream>
#include<string.h>
#include<map>
#include<list>
#include<string>
#include<queue>
#include<set>
#include<vector>
#include<stack>
#include<limits>
#define re register
#define iosgo() std::ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
#define run(i,n) for (int i = 1; i <= n; i++)
#define cin std::cin
#define cout std::cout
#define ll long long
#define endl "\n"
#define FOR(l,n) for(int i=1;i<=n;i++)
using namespace std;
typedef pair<int, int>pll;
pll c[200005];
int h[150][150];
ll x[200005], y[200005],s[200005];
ll gcd(ll a, ll b)
{
    return b ? gcd(b, a % b) : a;
}
signed main()
{
    iosgo();
    int t; cin >> t;
    while (t--)
    {
        memset(x, 0, sizeof(x));
        memset(y, 0, sizeof(y));
        memset(s, 0, sizeof(s));
        int n, c;
        cin >> n >> c;
        run(i, n)cin >> x[i], y[i] = min(x[i] + i, x[i] + n - i + 1);//将y取最优值
        sort(y + 1, y + n + 1);//取完排序
        run(i, n)s[i] = s[i - 1] + y[i];//利用前缀和查询最优解。(由于传送位置的设定)
        int ans = 0;
        run(i, n)
        {
            if (x[i] + i > c)continue;//如果最初的传送门都用不了就跳过
            int k = upper_bound(s + 1, s + n + 1, c - (x[i] + i)) - (s + 1);
            //查询用完之后的对应前缀和的最大值。这里返回的位置是 - (s + 1),就是前缀和的下标。即答案
            int yy = min(x[i] + i, x[i] + n - i + 1);
            if (y[k] >= yy)//由于y是排好序的,如果你最多用到第k个传送门但是y[k]却>=yy,意思就是你使用               //的初始传送门是被算进了这个前缀和里面的,所以要加上这个值重找一边
            {
                 k = upper_bound(s + 1, s + n + 1, c + yy - (x[i] + i)) - (s + 1);
                 ans = max(ans, k);
            }
            else
            {
                ans = max(ans, k+1);
            }
        }
        cout << ans << endl;
    }
}

PS : 这题是向一位大佬学习的:https://blog.csdn.net/JCGOODER/article/details/128886262?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522167619752816782429794488%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=167619752816782429794488&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-128886262-null-null.142^v73^control,201^v4^add_ask,239^v1^insert_chatgpt&utm_term=Teleporters%20%28Hard%20Version%29&spm=1018.2226.3001.4187

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值