CodeForces Round #705 总结&题解
突然发现博客咕了好久了,今天更新一下~~
A. Anti-knapsack
给定整数 \(n,k\),从\(\{1,2,3,\cdots,n\}\) 中选出最大的子集 \(S\),使得 \(\nexists A\subseteq S,\sum\limits_{x \in A}x=k\)。
显然,\(\gt k\) 的随便选,\(\lt k\) 的选 \(\lceil\frac{k}{2}\rceil,\lceil\frac{k}{2}\rceil+1,\cdots,k-1\) 即可。可以从可行性及最优性两方面证明。
Code:(考场写的,比较潦草)
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read(){
T x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
#define rdi read<int>
#define rdl read<ll>
int n,k,T;
int main(){
T=rdi();
while(T--){
n=rdi(),k=rdi();
int cnt=0,a[1010]={0};
for(int i=k+1;i<=n;i++) a[++cnt]=i;
for(int i=(k+1)/2;i<k;i++) a[++cnt]=i;
printf("%d\n",cnt);
for(int i=1;i<=cnt;i++) printf("%d ",a[i]);
puts("");
}
return 0;
}
B. Planet Lapituletti
假设一天有 \(h\) 小时,每小时 \(m\) 分钟。给定一个起始时间 (HH:MM形式),求从起始时间起第一个合法的镜像时间。
合法时间:该时间以数位管形式显示时,\(0 \le 分钟数 \lt m\),\(0 \le 时钟数 \lt m\)。
合法镜像时间:该时间是合法时间,且该时间在镜子中投影(即左右翻转)后仍可以作为一个合法时间。
打表出 \(0\sim 9\) 对应镜像数字的表(不合法为 \(-1\)),然后枚举时间即可。
一个坑:\(00:01\) 可以到 \(00:00\)(相当于到了第二天)。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
template<typename T>
inline T read(){
T x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
#define rdi read<int>
#define rdl read<ll>
int T,n,m,x,y;
const int fx[]={0,1,5,-1,-1,2,-1,-1,8,-1};
pii rev(int x,int y){
int a=x/10,b=x%10,c=y/10,d=y%10;
if(fx[a]==-1||fx[b]==-1||fx[c]==-1||fx[d]==-1) return make_pair(-1,-1);
return make_pair(fx[d]*10+fx[c],fx[a]+fx[b]*10);
}
int main(){
T=rdi();
while(T--){
n=rdi(),m=rdi();
scanf("%d:%d",&x,&y);
for(;;y++,(x+=y/m)%=n,y%=m){
if(rev(x,y).first==-1) continue;
if(rev(x,y).first<n&&rev(x,y).second<m){
printf("%02d:%02d\n",x,y);
break;
}
}
}
return 0;
}
C. K-beautiful Strings
给定一个长度为 \(n\) 的字符串 \(s\) 及一个常数 \(k\),求字典序大于等于 \(s\) 、长度为 \(n\) 的字典序最小的好的字符串。
好的字符串:由小写字母构成,且每种字符都出现了 \(k\) 的倍数次。
既然要求字典序大于等于 \(s\) 且字典序最小,那么可以从后往前枚举前缀。
假设当前枚举到了 \(i\),先从 \(s_i\) 开始枚举当前这一位取什么字符,然后统计 \(s_{1\dots i}\) 中每种字符的出现次数,设为 \(cnt\),那么显然,后面 \(s_{i+1\dots n}\) 要补充 \(sum=\sum\limits_{i='a'}^{'z'} ((k-cnt_i\bmod k)\bmod k)\) 个字符,此时存在合法字符串,当且仅当 \(sum \le n-i\) 且 $ sum \equiv n-i \pmod k$。如果满足,我们就先填充 \(sum-(n-i)\) 个字符 ‘a’ ,然后从 ‘a’ 到 ‘z’ 依次处理当前字符需填充数量并填充即可。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
template<typename T>
inline T read(){
T x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
#define rdi read<int>
#define rdl read<ll>
const int N=100010;
int T,n,k;
char s[N];
int main(){
T=rdi();
while(T--){
n=rdi(),k=rdi(),scanf("%s",s+1);
if(n%k){
puts("-1");
continue;
}
int cnt[30]={0};
for(int i=1;i<=n;i++) cnt[s[i]-'a']++;
bool flg=1;
for(int j=0;j<26;j++) flg&=((cnt[j]%k)==0);
if(flg){
printf("%s\n",s+1);
continue;
}
for(int i=n;i>=1;i--){
int cur=s[i]-'a',success=0;
for(int j=cur+1;j<=26;j++){
cnt[cur]--;cnt[j]++;s[i]=j+'a';
int kkk=0;
for(int j=0;j<26;j++){
if(cnt[j]%k) kkk+=(k-cnt[j]%k)%k;
}
if(kkk<=n-i&&(n-i-kkk)%k==0){
int pos=i;
for(int o=1;o<=(n-i-kkk)-(n-i-kkk)%k;o++) s[++pos]='a';
for(int o=0;o<26;o++){
if(cnt[o]%k){
for(int p=1;p<=(k-cnt[o]%k);p++) s[++pos]=o+'a';
}
}
success=1;
goto ed;
}
cnt[cur]++,cnt[j]--;s[i]=cur+'a';
}
cnt[s[i]-'a']--;
}
// char cur=s[0]+1;
// for(int i=1;i<=k;i++) s[i]=
ed:
printf("%s\n",s+1);
}
return 0;
}
D. CF1493D GCD of an Array
给定一个序列 \(a\),要求支持每次将某个位置 \(i\) 乘上一个数并询问当前整个序列的 \(\gcd\)。
先筛一下质数,开一些 \(multiset\) ,第 \(i\) 个维护 \(a\) 中每个数分解质因数后 \(1\sim 2\times10^5\) 中第 \(i\) 大的质数的指数。
修改将要乘的数分解质因数后直接加上即可。但查询若把 \(1\sim 2\times10^5\) 每个质数都查询一次最小值肯定会TLE,所以要维护答案的变化量,只查询被修改过的对应的质数。
Code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
template<typename T>
inline T read(){
T x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
}
#define rdi read<int>
#define rdl read<ll>
const int N=200010,MOD=1e9+7,INF=0x3f3f3f3f,P=18010;
int n,q,a[N];
int p[N],cp,vis[N];
void pr(int lim){
vis[0]=vis[1]=1;
for(int i=2;i<=lim;i++){
if(!vis[i]){
p[++cp]=i;
for(int j=i;j<=lim/i;j++) vis[i*j]=1;
}
}
}
ll qpow(ll x,ll y){
ll res=1;
while(y){
if(y&1) (res*=x)%=MOD;
(x*=x)%=MOD;y>>=1;
}
return res;
}
multiset<int> s[P];
map<int,int> fac[N],tmp;
ll ans=1;
ll gcd(ll x,ll y){return !x?y:gcd(y%x,x);}
int main(){
n=rdi(),q=rdi();
for(int i=1;i<=n;i++){
a[i]=rdi();
if(i==1) ans=a[i];
else ans=gcd(ans,a[i]);
}
pr(N-10);
for(int i=1;i<=n;i++){
int lim=sqrt(a[i]),x=a[i];
for(int j=1;p[j]<=lim&&j<=cp;j++){
if(x%p[j]==0){
int cnt=0;
while(x%p[j]==0) x/=p[j],cnt++;
fac[i][j]=cnt;
}
lim=sqrt(x);
}
if(x>1){
int tmp=lower_bound(p+1,p+cp+1,x)-p;
fac[i][tmp]++;
}
for(auto x:fac[i]) s[x.first].insert(x.second);
}
while(q--){
int id=rdi(),val=rdi();
int lim=sqrt(val),x=val;tmp.clear();
for(int j=1;p[j]<=lim&&j<=cp;j++){
if(x%p[j]==0){
int cnt=0;
while(x%p[j]==0) x/=p[j],cnt++;
tmp[j]=cnt;
}
lim=sqrt(x);
}
if(x>1){
int posss=lower_bound(p+1,p+cp+1,x)-p;
tmp[posss]++;
}
for(auto x:tmp){
int x1=x.first,x2=x.second;
int pre=fac[id][x1],post=pre+x2;
fac[id][x1]+=x2;
int minx1=s[x1].size()<n?0:(*s[x1].begin());
if(pre) s[x1].erase(s[x1].find(pre));
s[x1].insert(post);
int minx2=s[x1].size()<n?0:(*s[x1].begin());
ans=ans*qpow(p[x1],minx2-minx1)%MOD;
}
printf("%lld\n",ans);
}
return 0;
}
完结撒花~~