Codeforces Round #705 (Div. 2)

A. Anti-knapsack

题目大意

给出两个整数n,k,要求您从1-n中选择最多不同整数构成的数组,并不存在总和等于k的子集。

要求输出数组大小并依次输出数组中的数。如果不存在就输出0

解题思路

用i遍历1-n,如果i大于k则直接把i存入数组中,否则就将k - i存入数组中,并标记i和k-i。最后输出整个数组即可。

#include <iostream>
#include <map>
#include <cstring>
using namespace std;
const int N = 1e3 + 100;
int a[N];
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        map<int,int> ma;
        memset(a,0,sizeof(a));
        int j = 0;
        int n,k;
        cin >> n >> k;
        for(int i = 1 ; i <= n ; i++)
        {
            if(!ma[i] && k - i != 0)
            {
                if(k - i > 0 && k - i <= n)
                {
                    a[j++] = k - i;
                    ma[i] = 1;
                    ma[k - i] = 1;
                }else if(k < i)
                {
                    a[j++] = i;
                }
            }
        }
        cout << j << endl;
        for(int i = 0 ; i < j ; i++)
        {
            cout << a[i] << " ";
        }
        cout << endl;
    }
    return 0;
}

B. Planet Lapituletti

题目大意

对于给定h和m,分别表示一天有h小时,一小时有m分钟,并在下一行给出hh:mm制的时间,让您判断其镜面时间是否合法
在这里插入图片描述
如图所示,对于一天24小时,一小时60分钟的制度来说,第一个时钟的镜面不是合法的时钟,第二个05:11的镜面对应的是11:20是合法的时钟。
对于不合法的时钟要将其恢复成最近的合法时钟并输出,对于合法的时钟直接输出即可

解题思路

先将九个数字预处理,0,1,8的镜面对应都是其本身,2的镜面对应5,5的镜面对应2,其他数字的镜面都是不合法的数字。

int a[10] = {0,1,5,-1,-1,2,-1,-1,8,-1};

将规定的小时存入h变量,分钟存入m变量,以字符串的形式读入时钟,并将时钟的小时分钟转换为整数hh和mm,每次都判断hh和mm是否合法,即判断其对应的数字的镜面是否是合法的,如果合法则需要判断将镜面恢复成非镜面后判断是否是合法的时钟。如果合法则按规定格式输出否则继续将m++

#include <bits/stdc++.h>
using namespace std;
int a[10] = {0,1,5,-1,-1,2,-1,-1,8,-1};
const int Inf = 1e9;
int get(int x)
{
    string s = to_string(x);
    if(s.size() == 1) s = "0" + s;
    string ans = "";
    for(int i = 1 ; i >= 0 ; i--)
    {
        if(a[s[i] - '0'] == -1) return Inf;
        ans += char(a[s[i] - '0'] + '0');
    }
    return stoi(ans);
}
string good(int x)
{
    string ans = to_string(x);
    if(x < 10) ans = "0" + ans;
    return ans;
}
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        int h,m;
        string s;
        cin >> h >> m;
        cin >> s;
        int hh = (s[0] - '0') * 10 + s[1] - '0';
        int mm = (s[3] - '0') * 10 + s[4] - '0';
        while(1)
        {
            if(mm == m)
            {
                hh++;
                mm = 0;
            }
            if(hh == h)
            {
                hh = 0;
            }
            if(get(mm) < h && get(hh) < m)
            {
                cout << good(hh) << ":" << good(mm) << endl;
                break;
            }
            mm++;
        }
    }
    return 0;
}

C. K-beautiful Strings

题目大意

对于给定字符串长度以及整数k,如果该字符串的每一个字母总和都能被k整除,那么就称这个字符串为beautiful string。
现在给定一个字符串求字典序大于等于原串的beautiful string,如果不存在 则输出-1

解题思路

分析题意得:

  1. 如果n%k不等于0,那么一定不存在beautiful string,直接输出-1
  2. 如果n是k的倍数,那么就首先统计出每个字母出现的次数,存入cnt数组中,再根据统计结果算出每个字母需要填补多少个才能变成k的倍数即(k - x % k) % k,将每个字母缺的个数加起来得到sum。
    如果sum = 0则说明这个字符串本身就是beautiful string,直接输出原串即可
    否则从后往前贪心,将当前字母删掉并从当前字母s[i] + 1遍历到’z’判断选择哪个字母会使得i + 1 + sum <= n,然后输出前i个字母,再输出当前遍历到的字母,然后输出n - i - 1 - sum个’a’字符,最后遍历26个字母根据cnt数组输出还需要凑k的倍数的字母
#include <iostream>
#include <map>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
char str[N];
string s;
int get(int x,int k)
{
    return (k - x % k) % k;
}
int main()
{
    int t;
    cin >> t;
    while(t--)
    {
        bool flag = false;
        int n,k;
        cin >> n >> k;
        int cnt[27] = {};
        cin >> s;
        if(n % k != 0)
        {
            cout << "-1" << endl;
            continue;
        }
        int sum = 0;
        for(int i = 0 ; i < s.size() ; i++)
        {
            cnt[s[i] - 'a']++;
        }
        for(int i = 0 ; i < 26 ; i++)
        {
            sum += get(cnt[i],k);
        }
        if(sum == 0)
        {
            cout << s << endl;
            continue;
        }
        for(int i = n - 1 ; i >= 0 ; i--)
        {
            sum = sum - get(cnt[s[i] - 'a'],k) + get(--cnt[s[i] - 'a'],k);
            for(int j = s[i] - 'a' + 1 ; j < 26 ; j++)
            {
                int last = sum;
                sum = sum - get(cnt[j],k) + get(++cnt[j],k);
                if(i + 1 + sum <= n)
                {
                    for(int g = 0 ; g < i ; g++)
                    {
                        cout << s[g];
                    }
                    cout << char(j + 'a');
                    for(int g = 1 ; g <= n - sum - (i + 1) ;g++)
                    {
                        cout << 'a';
                    }
                    for(int w = 0 ; w < 26 ; w++)
                    {
                        int f = get(cnt[w],k);
                        while(f--) cout << char(w + 'a');
                    }
                    cout << endl;
                    flag = true;
                    break;
                }
                sum = last;
                cnt[j]--;
            }
            if(flag) break;
        }
    }
    return 0;
}

D. GCD of an Array

题目大意

给出n个数和q个查询,每次查询都输入i和x,即n个数中的第i个数乘x,最后求出q个查询之后n个数的最大公约数

解题思路

分析题意可知,如果直接暴力做那必然不行。
由算术基本定理可知,一个数可以由唯一若干个质数的αi次方相乘得到。即:在这里插入图片描述
那么由此我们可以推出n个数的最大公约数即这n个数公共质因子的最小次幂即pαmin依次相乘。也就是说,如果一个质因子p要对这n个数最大公约数产生贡献,那么这个p就是这n个数的公共质因子,那么p的贡献就是出现过的最小次幂pαmin
对于这道题,我们可以对p出现的幂次开一个multiset,对于每个a[i]我们都将pα插入multiset中,multiset会自动排序且不去重,可以省很多时间。如果set[p].size()==n,那么就说明p是n个数的公共质因子,那么我们就用set[p].begin()取出其最小次幂,答案乘上pset[p].begin()-pre[p],pre[p]是上一次p对答案的贡献,其初始值为0。

#include <iostream>
#include <set>
#include <map>
using namespace std;
typedef long long ll;
const int N = 301000;
const ll mod = 1e9 + 7;
int primes[N],minp[N],a[N],pre[N];
int cnt;
ll ans = 1;
bool s[N];
map<int,int>mp[N];//m[i][j]表示第i个数的质因子j的幂次
multiset<int>st[N];
void get_primes(int n) //提前预处理筛出素数和每个数的最小质因子
{
    for(int i = 2 ; i <= n ; i++)
    {
        if(!s[i])
        {
            primes[cnt++] = i;
            minp[i] = i;//质数的最小质因子是他自己
        }
        for(int j = 0 ; primes[j] * i <= n ; j++)
        {
            s[primes[j] * i] = true;
            minp[primes[j] * i] = primes[j];
            if(i % primes[j] == 0)
            {
                break;
            }
        }
    }
}
ll pow_mod(ll x,ll k,ll mod)//运用快速幂来求解
{
    ll res = 1;
    while(k)
    {
        if(k & 1) res = res * x % mod;
        k >>= 1;
        x = x * x % mod;
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(false);
	cin.tie(0) , cout.tie(0);
    get_primes(N - 1);//先预处理N - 1以内的质数以及每个数的最小质因子
    int n,q;
    cin >> n >> q;
    for(int i = 1 ; i <= n ; i++) cin >> a[i];
    for(int i = 1 ; i <= n ; i++)//首先对a[i]中的质因子进行预处理
    {
        int x = a[i];
        while(x > 1)
        {
            int p = minp[x];
            int j = 0;//p在x中的幂次
            while(x % p == 0)
            {
                x /= p;
                j++;
            }
            mp[i][p] = j;
            st[p].insert(j);
            if(st[p].size() == n)//如果是公共质因子那么就可以乘到答案中
            {
                int j = *st[p].begin();//取出最小次幂
                ans = ans * pow_mod(1ll * p,1ll * j,mod) % mod;
                pre[p] = j;//存下p在这次做出的贡献
            }
        }
    }
    while(q--)
    {
        int i,x;
        cin >> i >> x;
        while(x > 1)
        {
            int j = 0;
            int p = minp[x];
            while(x % p == 0)
            {
                x /= p;
                j++;
            }
            if(mp[i].count(p))//如果p之前存在过那么就先消除其影响
            {
                auto it = st[p].find(mp[i][p]);
                st[p].erase(it);
            }
            mp[i][p] += j;//x中p的幂次加入再插入st[p]中
            st[p].insert(mp[i][p]);
            if(st[p].size() == n)//如果p是公共质因子那么就存入答案
            {
                int t = *st[p].begin();
                ans = ans * pow_mod(1ll * p,(1ll * t - 1ll * pre[p]) , mod) % mod;//需消除之前的贡献
                pre[p] = t;//更新p的贡献
            }
        }
        cout << ans << endl;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值