题解 Codeforces Round 834 (Div. 3) / CF1759A~G
过了 ABCDEF,G 不会。
毕业了。以后打 Div.3 可能就是 unoffical 了。我也希望是这样的。
A. Yes-Yes?
https://codeforces.com/contest/1759/problem/A
problem
判断给定的字符串是否为无穷个 Yes
拼接组成的字符串的连续子串。\(|S|\leq 50\)。
solution
暴力。
具体地,判断 \(S,Ye+S,Y+S\) 是否有一个是合法的。
code
点击查看代码
typedef long long LL;
string t="Yes";
bool check(string s){
for(int i=0;i<s.size();i++) if(t[i%3]!=s[i]) return 0;
return 1;
}
int main(){
string s;
for(scanf("%*d");cin>>s;puts(check(s)||check("Ye"+s)||check("Y"+s)?"yes":"no"));
return 0;
}
B. Lost Permutation
https://codeforces.com/contest/1759/problem/b
problem
判断是否存在一个排列 \(p\) 使得:\(b\) 是它的子序列;\(\sum_ip_i-\sum_ib_i=s\)。\(n\leq50,s\leq 1000\)。
solution
枚举 \(p\) 的长度。
不必每次都暴力填满,只需要开一个桶,在值域上从小往大扫即可。
code
点击查看代码
typedef long long LL;
int n,s,t[1010];
int mian(){
for(int i=1,x;i<=n;i++) scanf("%d",&x),t[x]++;
int maxk=0;
for(int i=1;i<=100;i++) if(t[i]>1) return puts("no"),0; else if(t[i]) maxk=i;
while(t[maxk]) maxk--;//你需要保证 max b_i <= ans
for(int i=1;i<=1000;i++){
if(!t[i]) s-=i;
if(s<0) return puts("no"),0;
else if(!s&&i>=maxk) return puts("yes"),0;
}
puts("no");
return 0;
}
C. Thermostat
https://codeforces.com/contest/1759/problem/c
problem
\(l,r,x\),\(a\) 是当前温度,\(b\) 是目标温度。有操作:选定一个 \(y\),满足 \(|y-a|\geq x\land l\leq y\leq r\),并令 \(a\gets y\)。求使得 \(a=b\) 的最小操作次数。
\(|l|,|r|,x,|a|,|b|\leq 10^9\)。
solution
因为这个东西跳的长度是没有限制的。跳到 \([l,r]\) 内和 \(l,r\) 没有什么区别。
故判断:\(a=b,a\to b,a\to l\to b,a\to r\to b,a\to l\to r\to b,a\to r\to l\to b\) 里面是否有合法的路径即可。记得特判 \(0\)。
code
点击查看代码
typedef long long LL;
LL l,r,x,a,b;
bool check(LL p){return l<=p&&p<=r;}
bool jump(LL a,LL b){return check(b)&&abs(a-b)>=x;}
int mian(){
if(a==b) return 0;
if(jump(a,b)) return 1;//一步到位已结束
if(jump(a,l)&&jump(l,b)) return 2;
if(jump(a,r)&&jump(r,b)) return 2;//尝试跳到 b,贪心跳左右端点
if(jump(a,r)&&jump(r,l)&&jump(l,b)) return 3;
if(jump(a,l)&&jump(l,r)&&jump(r,b)) return 3;
return -1;
}
D. Make It Round
https://codeforces.com/contest/1759/problem/d
problem
给定 \(n,m\),求一个正整数 \(k\) 使得 \(n\cdot k\) 有最多的后导零,在此基础上使 \(k\) 最大。\(n,m\leq 10^9\)。
solution
考虑到后导零最多有 \(18\) 个。枚举它。
考虑将 \(n\) 中的因数 \(2,5\) 拎出来,记 \(n=x\cdot 2^p\cdot 5^q\)。那么我们优先在 \(k\) 中放 \(2\) 去填 \(5^q\),再放 \(5\) 去填 \(2^p\),最后堆 \(10\),这样就能使后导零的数量最多。
code
点击查看代码
typedef long long LL;
LL qpow(LL a,LL b){LL r=1;for(;b;b>>=1,a=a*a) if(b&1) r=r*a; return r;}
LL n,m,cnt[10];
LL f(LL d){
LL res=1;
res*=qpow(2,min(cnt[5],d)),d-=min(d,cnt[5]);
res*=qpow(5,min(cnt[2],d)),d-=min(d,cnt[2]);
res*=qpow(10,max(d,0ll));
return res;
}
int mian(){
memset(cnt,0,sizeof cnt);
int bn=n;
while(n%2==0) n>>=1,cnt[2]++;
while(n%5==0) n/=5,cnt[5]++;
if(cnt[2]<cnt[5]) cnt[5]-=cnt[2],cnt[2]=0;
else cnt[2]-=cnt[5],cnt[5]=0;
for(int i=0;i<=18;i++){
LL del=f(i);
if(del>m){
del=f(--i);
del*=m/del;
printf("%lld\n",del*bn);
return 0;
}
}
printf("%lld\n",n*m);
return 0;
}
E. The Humanoid
https://codeforces.com/contest/1759/problem/e
problem
\(n\) 个怪物,力量为 \(a_i\),你的力量值为 \(h\),初始有两瓶蓝药,一瓶绿药,每秒钟可以操作:
- 找一个 \(i\),若 \(a_i<h\),删除 \(a_i\),\(h\gets h+a_i/2\)。
- 喝掉一瓶蓝药,\(h\gets 2h\)。
- 喝掉一瓶绿药,\(h\gets 3h\)。
求最多能干掉多少个怪物。\(n\leq 10^5,h\leq 10^9\)。
solution
考虑如果知道喝药的顺序,可以对着 \(a_i\) 排序,从小到大逐个解决,打不掉就喝药。
注意到药是有限的,枚举喝药顺序就行。
code
点击查看代码
typedef long long LL;
int n;
LL a[200010],h;
LL solve(vector<int> ccf,LL now){
int pos=0;
for(int i=1;i<=n;i++){
while(a[i]>=now&&pos<ccf.size()) now*=ccf[pos++];
if(a[i]>=now) return i-1;
now+=a[i]/2;
}
return n;
}
int mian(){
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
sort(a+1,a+n+1);
printf("%lld\n",max({solve({2,2,3},h),solve({2,3,2},h),solve({3,2,2},h)}));
return 0;
}
F. All Possible Digits
https://codeforces.com/contest/1759/problem/f
problem
\(p\) 进制数 \(x\),写出 \(x,x+1,x+2,⋯,x+k\) 使得写完之后 \(0\sim(p-1)\) 的数位都出现至少一次,最小化 \(k\)。
\(x\) 有 \(10^5\) 位,\(2\leq p\leq 10^9\)。
solution
观察到答案最多为 \(p-1\),进位只会发生一次。
二分答案,然后每一位上出现的数位是两段连续的区间,欲求这些区间的并,贪心即可(按左端点排序)。
code
点击查看代码
typedef long long LL;
pair<LL,LL> seq[210];
int n,a[110],b[110];
LL p;
bool check(LL k){
int cnt=0; memcpy(a,b,sizeof b);
auto out=[&](int l,int r){seq[++cnt]={l,r};};
auto write=[&](){if(a[1]) out(a[1],a[1]);for(int i=2;i<n;i++) out(a[i],a[i]);};
write();
out(a[n],min(a[n]+k,p-1));
if(a[n]+k>=p){
k-=p-a[n],out(0,k),a[n-1]++;
for(int i=n-1;i>=1;i--) if(a[i]>=p) a[i]-=p,a[i-1]++;
write();
}
return [&]()->bool{
sort(seq+1,seq+cnt+1);
if(seq[1].first>0) return 0;
LL now=0;
for(int i=1;i<=cnt;i++){
if(now+1<seq[i].first) return 0;
now=max(seq[i].second,now);
}
return now==p-1;
}();
}
LL bianry(LL l,LL r){
LL ans=0;
for(LL mid=(l+r)>>1;l<=r;mid=(l+r)>>1){
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
return ans;
}
int mian(){
n++,b[1]=0;
for(int i=2;i<=n;i++) scanf("%d",&b[i]);
printf("%lld\n",bianry(0,p-1));
return 0;
}
G. Restore the Permutation
https://codeforces.com/contest/1759/problem/g
problem
给定长为 \(n/2\) 的数组 \(b\),试求出字典序最小的排列 \(p\) 使得 \(b_i=\max_{p_{2i-1},p_{2i}}\),或者报告无解。\(n\leq 10^5\)。
solution
考虑显然的结论:\(p_{2i}=b_i\)。这时 \(p_{2i-1}<p_{2i}\) 很优。
这时相当于是剩下一些数,要去匹配这些 \(p_{2i}\)。
法一(错误)
考虑构造一组解:从小到大扫,然后匹配遇到的第一个。
然后考虑从值域出发,从小到大将一个更小的值换到前面去。可惜是 \(O(n^2)\) 的。
法二
考虑倒着扫。
因为字典序最小,也等价于大的数字尽量在后面。
倒着枚举数值,维护一个堆,存放可以放的位置,每次拿最大的位置放。
code
点击查看代码
typedef long long LL;
int n,a[200010],pos[200010];
int mian(){
for(int i=1;i<=n;i++) pos[i]=0;
for(int i=2;i<=n;i+=2) scanf("%d",&a[i]),pos[a[i]]=i-1;
priority_queue<int> q;
for(int i=n;i>=1;i--){
if(pos[i]) q.push(pos[i]);
else{
if(q.empty()) return puts("-1"),0;
a[q.top()]=i,q.pop();
}
}
for(int i=1;i<=n;i++) printf("%d%c",a[i]," \n"[i==n]);
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/solution-cf1759.html