Codeforces Round 964(Div.4)赛后题解(A~G)

A题

题意

给定一个字符串,求两个数字字符的和

思路

直接减字符0相加即可

代码

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=1e5+10;
const LL INF=1e18;
const double small=1e-16;
//千万不要用puts()和gets(),求求你了 


void solve()
{
	string s;
	cin>>s;
	cout<<s[0]-'0'+s[1]-'0'<<endl;
}

int main()
{
    
	int t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

B题

题意

两个人比赛,每人一开始有两张1~10的牌,比赛两回合,每回合各种翻一张牌,牌上数字严格大的获胜,相等则平局;赢的回合数严格大的获得种游戏胜利,求所有情况中A获胜的种数有多少

思路

4个for循环枚举所有情况,然后统计每一局游戏的回合情况:赢两场或胜一场平一场都为A胜(赛时没有考虑到平局情况,导致wa了一发)

代码

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=1e5+10;
const LL INF=1e18;
const double small=1e-16;
//千万不要用puts()和gets(),求求你了 
int a[5],b[5];

void solve()
{
	for(int i=1;i<=2;i++)cin>>a[i];
	for(int j=1;j<=2;j++)cin>>b[j];
	LL res=0;
	for(int i=1;i<=2;i++)
	{
		
		for(int j=1;j<=2;j++)
		{
		    for(int k=1;k<=2;k++)
		    {
		    	if(k==i)continue;
		    	for(int y=1;y<=2;y++)
		    	{
		    		if(j==y)continue;
		    		int cnt=0;
		    		int p=0;
		    		if(a[i]>b[j])cnt++;
		    		else if(a[i]==b[j])p++;
		    		if(a[k]>b[y])cnt++;
		    		else if(a[k]==b[y])p++;
		    		if(cnt==2||(p==1&&cnt==1))res++;
				}
			}
		}
	//	if(cnt==2)res++;
	}
	cout<<res<<endl;
}

int main()
{
    
	int t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

C题

题意

一天有m小时,但是A有n个任务占据n个不重叠的时间段,询问能否抽出s分钟洗澡

思路

将时间段按照升序排序,因为两两不重叠,所有可以直接枚举时间段,看两个时间段之间的空隙是否能够大于或等于s。

代码

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=2e5+10;
const LL INF=1e18;
const double small=1e-16;
//千万不要用puts()和gets(),求求你了 
PII a[N];
void solve()
{
	int n,m,k;
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++)
	{
		LL x,y;
		cin>>x>>y;
		a[i]={x,y}; 
	}
	sort(a+1,a+1+n);
	if(a[1].first>=m)
	{
		cout<<"YES"<<endl;
			return;
	}
	if(k-a[n].second>=m)
	{
		cout<<"YES"<<endl;
			return;
	}
	if(n>1)
	for(int i=1;i<n;i++)
	{
		int l=a[i].second,r=a[i+1].first;
		if(r-l>=m)
		{
			cout<<"YES"<<endl;
			return;
		}
	}
	cout<<"NO"<<endl;
}

int main()
{
    
	int t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

D题

题意

给定一个由小写字母和问号组成的字符串,再给定一个模式串,求把字符串中的问号改成任意小写字符时能够使得模式串为字符串子序列的答案,如果没有符合条件答案,输出No,否则输出YES和正确答案;

思路

题目说是子序列,说明不用连续,直接用两个指针进行遍历,如果?,改成能够匹配的字符,如果是不能匹配的字符,重新匹配;如果匹配成功碰到问号,直接随便改,最后看两个指针的位置。

代码

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=1e5+10;
const LL INF=1e18;
const double small=1e-16;
//千万不要用puts()和gets(),求求你了 


void solve()
{
	string s,b;
	cin>>s>>b;
	int idx=0;
	for(int i=0;i<s.size();i++)
	{
		
		if(idx<b.size()&&s[i]==b[idx])
		{
			idx++;
			continue;
		}if(s[i]!='?')continue;
		if(idx<b.size())
		{
			s[i]=b[idx];
			idx++;
		}
		else s[i]='a';
	}
	//cout<<idx<<endl;
	if(idx<b.size())cout<<"NO"<<endl;
	else cout<<"YES"<<endl,cout<<s<<endl;
}

int main()
{
    
	int t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

E题 

题意

给定一个有n个非负整数组成集合,然后每一次操作可以选两个数a,b拿出,放入两个数a*3和b/3下取整(题目是四舍五入,但是下取整才能过,头疼,被硬控了),然后求使得集合内所有数字都为0的最少操作数。

思路

可以先把最小的数转化出一个0,然后再用这个0去处理其他不为0的数;所以先预处理出范围内所有的数不断/3到0的操作数,组成一个前缀和,最后因为在对l操作产生0时同时生成了等量的*3,所以l的操作数要*2,后面的用前缀和求即可。

代码

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=2e5+10;
const LL INF=1e18;
const double small=1e-16;
const LL MAX=2e5;
//千万不要用puts()和gets(),求求你了 
LL p[N];
LL s[N];

int work(int m)
{
	int cnt=0;
	while(m)
	{
		m/=3;
		cnt++;
	}
	return cnt;
}

void unit()
{
	for(int i=1;i<=MAX;i++)
	{
		int tmp=work(i);
		p[i]=tmp;
		s[i]=s[i-1]+p[i];
	}
}

void solve()
{
	int l,r;
	cin>>l>>r;
	cout<<s[r]-s[l-1]+p[l]<<endl;
}

int main()
{
    
	int t=1;
	unit();
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

F题

题意

给定一个由0和1构成的数字集,求长度为k的子序列(可以不连续)的中位数的和

思路

因为中位数为0的子序列对答案没有贡献,所以只需要求中位数为1的子序列的个数,又因为题目说了k一定是奇数,所以满足条件的子序列的1的个数一定大于0的个数,所以题目可以翻译为求长度为k,其中1比0多的子序列个数;由于题目可以排序,所以与选取的顺序无关,统计出0和1的个数然后求满足条件的组合数和即可,但是难点在于怎么求组合数,这里要用到数论中的费马小定理:用逆元求组合数,接下来我会汇总一下求组合数的几种方法以及适用的范围,先预告一下。

代码 

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=2e6+10;
const LL INF=1e18;
const double small=1e-16;
const LL mod=1e9+7;
//千万不要用puts()和gets(),求求你了 

LL fact[N],infact[N];

LL qim(LL a,LL b)
{
	LL res=1;
	while(b)
	{
		if(b&1)res=res*a%mod;
		a=a*a%mod;
		b>>=1;
	}
	return res;
}

void unit()
{
	fact[0]=infact[0]=1;
	for(int i=1;i<=N;i++)
	{
		fact[i]=fact[i-1]*i%mod;
		infact[i]=qim(fact[i],mod-2)%mod;
	}
}

LL C(LL a,LL b)
{
	if(a<b)return 0;
	return fact[a]*infact[b]%mod*(infact[a-b]%mod)%mod;
}


void solve()
{
	int n,k;
	cin>>n>>k;
	LL zero=0,one=0;
	for(int i=1;i<=n;i++)
	{
		int x;
		cin>>x;
		if(x==1)one++;
		else zero++;
	}	
	LL half=k/2+1;
	LL sum=0;
	for(LL i=half;i<=min((LL)k,one);i++)
	{
		
		sum=(sum+(C(one,i)%mod)*(C(zero,k-i)%mod))%mod;
	} 
	cout<<sum%mod<<endl;
}

int main()
{ 
	int t=1;
	unit();
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

G1题

G2题

题意

好玩的交互题:给定一个标尺,有一个未知的刻度消失,使得测量长度x时,如果x>=该刻度,测出来的是x+1,否则测出来的是x;然后给我们有限次数的询问:每次询问一个长方形的长和宽,给出用标尺测量后得到的长方形的面积,要我们猜消失的刻度的位置。

思路

G1的询问次数给得多,可以用二分;但是G2的询问次数少只能用三分,这里能用三分是因为这里的check函数可以有两个参数(a,b)通过测量出来的面积和实际面积(a*b,(a+1)*(b+1),a*(b+1))进行比较,可以判断消失刻度在三分区间内的哪个区间。

代码(二分,只能过G1)

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=1e5+10;
const LL INF=1e18;
const double small=1e-16;
//千万不要用puts()和gets(),求求你了 

bool check(int mid)
{
	 cout<<"? 1 "<<mid<<endl;
	 
	 fflush(stdout);
	 int a;
	 cin>>a;
	 if(a!=mid)return true;//如果等于实际值,说明目标刻度在右边,否则为本身或左边
	 else return false;
}

void solve()
{
	int l=1,r=1000;
	while(l<r)
	{
		int mid=(l+r)/2;
		if(check(mid))r=mid;向右延伸,封右
		else l=mid+1;
	}
	cout<<"! "<<l<<endl;
}

int main()
{
    
	int t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

代码(三分,可以过G1和G2)

#include <iostream>
#include <cstring>
#include <queue>
#include <map>
#include <set>
#include <algorithm>
#include <cmath>
#include <vector>
#include <stack>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;//开long long,求求你了,记得开long long 
const LL N=1e5+10;
const LL INF=1e18;
const double small=1e-16;
//千万不要用puts()和gets(),求求你了 

int check(int mid1,int mid2)
{
	 cout<<"? "<<mid1<<" "<<mid2<<endl;
	 fflush(stdout);
	 int a;
	 cin>>a;
	 int t1=mid1*mid2;
	 int t2=(mid1+1)*(mid2+1);
	 int t3=(mid1)*(mid2+1);
	 if(a==t1)return 1;
	 if(a==t2)return 2;
	 if(a==t3)return 3;
}

void solve()
{
	int l=1,r=1000;
	while(r-l>2)
	{
		int mid1=(2*l+r)/3;//三分的mid求法,学到新知识了
		int mid2=(l+2*r)/3;
		int op=check(mid1,mid2);
		if(op==1)//目标刻度在mid1和mid2右边
		{
			l=mid2;
		}
		else if(op==2)//目标刻度在mid1或mid1左边
		{
			r=mid1;
		}
		else if(op==3)//目标刻度在mid1到mid2之间
		{
			l=mid1;
			r=mid2; 
		}
		if(r-l==2)//如果只相差2时,需要特判
		{
			cout<<"? 1 "<<l+1<<endl;
			fflush(stdout);
			int tmp;
			cin>>tmp;
			if(tmp==l+1)l=l+1;//说明答案是l+2,也就是r
			else r=l+1;//说明答案是l+1
		}
	}
	cout<<"! "<<r<<endl;
}

int main()
{
    
	int t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值