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
解题思路
分析题意得:
- 如果n%k不等于0,那么一定不存在beautiful string,直接输出-1
- 如果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;
}