题解 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;
}
posted @ 2022-11-19 13:12  caijianhong  阅读(192)  评论(0编辑  收藏  举报