A、Anti-knapsack
题意:
给我们一个n,一个k(n,k<=1000),需要我们得到一个集合,集合的元素全部小于n,并且任意子集相加不等于k,而且这个集合元素相加尽可能大。
思路:
大于的数全取,小于的
的数只能取一半,所以取大的一半。
MyCode:
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+7;
typedef long long int ll;
#define eb emplace_back
#define ef emplace_front
#define ep emplace
#define fi first
#define se second
inline void solve() {
int n,k;
cin>>n>>k;
int l=(k+1)>>1;
if(n-l) {
cout<<n-l<<'\n';
for(int i=l;i<k;++i) cout<<i<<' ';
for(int i=k+1;i<=n;++i) cout<<i<<' ';
cout<<'\n';
}
else cout<<0<<'\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin>>t;
while(t--) solve();
} B、 Planet Lapituletti
题意:
某个星球上的时间和我们地球类似,但是每天有h个小时,每小时有m分钟(1<=h,m<=100)。当前时间为HH:MM,这时候有一面镜子,问从HH:MM开始往后,当镜子里的时间合理时,即镜子里的时间满足0<=H<h && 0<=M<m时(镜子里的时间是当前时间的反射),真实时间是多少,以HH:MM格式输出。
思路:
模拟
MyCode:
#include <bits/stdc++.h>
using namespace std;
int h,m;
int mp[10]={0,1,5,-1,-1,2,-1,-1,8,-1};
inline bool check(const vector<int> &a, const vector<int> &b) {
if(mp[a[1]]==-1||mp[a[0]]==-1||mp[b[0]]==-1||mp[b[1]]==-1) return false;
int hh=mp[a[1]]*10+mp[a[0]],mm=mp[b[1]]*10+mp[b[0]];
if(hh>=h||mm>=m) return false;
return true;
}
inline void solve() {
int hh,mm;
scanf("%d%d",&h,&m);
scanf("%d:%d",&hh,&mm);
vector<int>a(2),b(2);
while(1) {
for(int i=hh,j;i<h;++i) {
for(j=mm;j<m;++j) {
a[0]=i/10,a[1]=i%10;
b[0]=j/10,b[1]=j%10;
if(check(b,a)) {
printf("%02d:%02d\n",i,j);
return;
}
}
mm=0;
}
hh=0;
}
}
int main() {
int T; scanf("%d",&T);
while(T--) solve();
} C、K-beautiful Strings
题意:
给我们一个由小写字母组成的字符串S,长度为n(n<=1e5),然后给我们一个k(k<=n),要我们找出比字典序尽可能小但不小于 S 的字符串,这个字符串还需满足每种字母出现的个数都是k的倍数。
思路:
当字符串长度n不是k的倍数的时候,不存在这种字符串
当字符串s满足条件时,s就是需要求的字符串
在修改字符串s的一些字符串从而得到需要的字符串时,开始修改的部位一定越靠后越好,这样得到的字符串才能最小。用桶存每个字母出现的次数,从大到小枚举修改的部分,计算还需要修改几(假设需要修改sum个位置)个位置才能使
的字母的出现次数是k的倍数,同时用que保存出现次数不是k的倍数的字母。如果sum小于
,那么就得不到需要的字符串。用nex数组维护每个字母还需要加几个才能凑齐k的倍数
情况一:当时,如果出现次数不是k的倍数的字母中最大的字母x大于s[i],那么直接把s[i]改成que中第一个大于s[i]的字母,同时修改这个字母的nex数组,以及sum--,表示已经修改了一次了;
情况二:当时,因为n是k的倍数,所以此时
,也就是说我可以构造k个
,所以直接把s[i]改成s[i]+1,同时修改s[i]+1的nex数组,以及sum--,需要注意的是,可能s[i]+1的nex数组已经是k的倍数了,所以如果此时
可能会是负数,如果减成了负数,那么
即可。
优先判断情况二(保证求得的字符串最小),处理完情况一或情况二后,把多余的字母修改成'a',然后从小到大修改成需要增加的字母。
MyCode:
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+7,maxm=2e5+7;
typedef long long int ll;
#define eb emplace_back
#define ef emplace_front
#define ep emplace
#define fi first
#define se second
int n,k,cnt[30],que[30],nex[30],tot,sum;
char s[maxn];
inline bool check(int r) {
sum=tot=0;
for(int i=1; i<=26; ++i) if(cnt[i]%k) {
sum+=k-cnt[i]%k;
que[++tot]=i;
}
return sum<=n-r+1;
}
inline void print(int r) {
for(int i=1; i<=r; ++i) printf("%c",s[i]);
}
int main() {
// ios::sync_with_stdio(false);
// cin.tie(nullptr);
int T;
scanf("%d",&T);
while(T--) {
scanf("%d%d",&n,&k);
scanf("%s",s+1);
if(n%k) {
puts("-1");
continue;
}
for(int i=1; i<=26; ++i) cnt[i]=0;
for(int i=1; s[i]; ++i) cnt[s[i]-'a'+1]+=1;
if(check(n+1)) {
print(n);
putchar('\n');
continue;
}
for(int i=n; i; --i) {
cnt[s[i]-'a'+1]-=1;
if(s[i]=='z') continue;
if(check(i) && (que[tot] > s[i]-'a'+1 || sum < n-i+1)) {
print(i-1);
for(int j=1; j<=26; ++j) if(cnt[j]%k) nex[j]=k-cnt[j]%k;
else nex[j]=0;
if(sum < n-i+1) {
printf("%c",s[i]+1);
--nex[s[i]-'a'+2],--sum;
if(nex[s[i]-'a'+2]<0) nex[s[i]-'a'+2]+=k,sum+=k;
} else {
for(int j=s[i]-'a'+2; j<=26; ++j) {
if(nex[j]) {
printf("%c",j+'a'-1);
--nex[j],--sum;
break;
}
}
}
nex[1]+=n-i-sum;
for(int j=1; j<=26; ++j) {
while(nex[j]--) printf("%c",(char)(j-1+'a'));
}
putchar('\n');
break;
}
}
}
} D、GCD of an Array
题意:
给一个数列,m次操作,每次将pos位置的数乘上x,问每次操作后数列的总gcd
思路:
求数列的总gcd会想到分解质因数,每个位置共有的质因数乘以这个质因数的最小个数,就是他们的总gcd。
将pos位置的数乘上x,其实就是给pos位置增加因子x,分解出x内的质因数,算贡献。
MyCode:
#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+7,maxm=4e4+7,mod=1e9+7;
#define eb emplace_back
int pre[maxn],prime[maxn],rev[maxn];
int n,ans=1;
unordered_map<int,int>mp[maxn];
vector<int>v[maxm];
inline void add(int x,int val) {
int t,y;
while(val!=1) {
t=pre[val];//先筛出最小的质因数
val/=t;
t=rev[t];//离散化
y=mp[x][t]++;//y+1表示 a[x]质因数t的个数
if(y==v[t].size()) v[t].emplace_back(0);//质因子t的最大个数增加,则新开一维去统计
if(++v[t][y]==n) ans=ans*1LL*prime[t]%mod;//又凑齐n个质因子t,最大公约数*t
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
for(int i=2,tot=0;i<maxn;++i) {
if(!pre[i]) {
prime[++tot]=pre[i]=i;
rev[i]=tot;
}
for(int j=1;j<=tot&&i*prime[j]<maxn;++j) {
pre[i*prime[j]]=prime[j];
if(i%prime[j]==0) break;
}
}
int Q,x,val;
cin>>n>>Q;
for(int i=1;i<=n;++i) {
cin>>x;
add(i,x);
}
while(Q--) {
cin>>x>>val;
add(x,val);
cout<<ans<<'\n';
}
} E、Enormous XOR
题意:
n是二进制串的长度
第一行是L的二进制表示
第二行是R的二进制表示
思路:
最差的情况就是取x=y=R,答案就是R
结论一:若L和R的最高位相同,那么答案为11...11
我只要取x=01...11,y=10...00即可。
结论二:若L和R最高位相同,R为奇数,答案是R
不会证
结论三:若L和R最高位相同,R为偶数,且,答案是R+1,反之是L
我只要取R、R-1、R-2就可以得到R+1
MyCode:
#include <bits/stdc++.h>
using namespace std;
int n,pos;
string l,r;
string add(string s) {
pos=n-1;
while(s[pos]=='1') {
s[pos]='0';
--pos;
}
s[pos]='1';
return s;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
cin>>l>>r;
if(l[0]!=r[0]) {
string c(n,'1');
cout<<c<<'\n';
return 0;
}
if(l==r||r[n-1]=='1'||add(l)==r)
cout<<r<<'\n';
else cout<<add(r)<<'\n';
} 
京公网安备 11010502036488号