CF-926(已更新:B-C)

CF-926

两点睡,七点起,阎王夸我好身体……

主要这场实在是难绷,两个小时都在C题上吊死了,也不是没想过跳题,只是后面的题我更是一点思路都没有-^-

“就喜欢这种被揭穿的感觉,爽!”

B

分析

​ 涂色的单元格能够包含k种对角线,很明显要根据图像的具体性质想答案:

然而我赛时是一股脑地猜结论,这种方法在赛时不确定性还是太大了,希望自己能尽快把思维这方面的短板补起来……

​ 上色时考虑单种特殊点对其它特殊点及答案的贡献。

  • 四个顶点:作图发现只有同侧的两个顶点对答案的贡献是2,另外两个是1;再分析发现,顶点包含的是一条最短和一条最长的对角线,前者只经过它自己,后者还要经过(n-1)个格子,所以上色一个顶点后会使(n-1)个处于最长对角线上的格子贡献-1
  • 中心点或者说不在边缘位置的点:对他们上色就会使经过它们包含的两种对角线的格子贡献-1——其中包括处于顶点,非边缘位置的点,边缘位置的格子
  • 边缘位置的点(不包括顶点):对他们上色就会使处于非边缘位置的格子贡献-1

​ 要使答案最小,我们涂色的格子的贡献一定尽量都是2,若贡献为2的格子涂完了再考虑贡献为1的格子,再结合上面三种点位的性质,我们发现涂顶点与边缘位置的格子才会使可选格子贡献值最大(涂色不在边缘位置的格子影响的格子数更多,涂它们比不涂它们会使贡献为2的格子更少)。由此再考虑使涂色这两种格子对其它格子影响最小,先涂的格子一定都在同一侧,有n个,而其余贡献为2的格子是对侧的边缘位置的格子,有(n-2)个,剩下的可选格子贡献都为1。

比如图示3*3的红色部分

操作

​ 因此,有w=(n*2-2)个格子贡献为2,(w乘2)>=k时,答案为(k/2)向上取整,否则,还要涂k-(w乘2)个贡献为1的格子,答案为k-(w乘2)+w=k-w

为了规避markdown语法……

代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
//int qz(int k){
//	if(k%2==0) return k/2;
//	else return k/2+1;
//}
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
	int t,n,k,w,ans=0;cin>>t;
	while(t--){
		cin>>n>>k;
		w=n*2-2;
        //身为蒟蒻现在才学会怎么向上取整很合理吧……
		if(w*2>=k) ans=(k+1)/2;//ans=qz(k);
		else ans=k-w;
		cout<<ans<<endl;
	}
	return 0;
}


C

分析

注意要认真读题!!!

因为没注意到连续这个条件,我赛时一半多时间都以为最多输x次-^-

​ 看了很多大佬的思路,好多是贪心和暴力,也感觉挺对的,但我赛时肯定是想不出这种解法思路,就是想通了也写不出代码……

其实代码大差不差,就是个人感觉思路挺难想的,比如贪心的策略是每(k-1)次下注的钱数指数级翻一番,比如k=2,下注钱数是1,2,4,8……k=3,是1,1,2,2,4,4,8……

​ 这里介绍一个我觉得好理解并且代码也好实现的思路(其实就是模拟,然后得到一个最优方案)。

​ 对于每次下注y个,

​ 两个条件:

  • 1<=y<=当前硬币数
  • 最多连续输x次

不意味着第x+1次必赢,万一前x次也有赢呢,所以只可推出每(x+1)轮必有至少一场赢

​ 两种当前硬币数的结果:

  • 赢:+(k-1)*y
  • 输:-y

题意是要我们找是否存在n,n属于任意整数,使得在每一轮都至少有n个硬币——因为n<=0的部分恒成立(当前硬币数一定>=0),实际上是看能否使硬币数的值在[1,正无穷)这一区间内线性对应

当时题意都没读懂

​ 假设下注i轮后,输了e次,第i轮硬币数=a+(i-e)乘((k-1)*y)-y乘e,这个式子中,i,e,y的取值都在[0,正无穷]内线性可取,彼此还互不影响,因此我们可以推广出: 只要能下注无穷次,一定能满足硬币数的值在[1,正无穷)这一区间内线性对应

这里我解释得挺抽象的,但意思是这么个意思,关键在于后面的思路……

​ 所以问题转化为如何能下注无穷次,无穷次中自然有无数个(x+1)轮,每轮执行的策略都应该相同,所以我们可以只考虑第一轮,也即使得第1到第x+1次每次下注后的硬币数>0,那如何实现呢?

操作

​ x+1轮里每次下注y之前,知道此次下注赢的话能得w=y*(k-1),还有输的话会损失y,以及之前已经输了sum。由于我们只知道x+1轮里必有赢,而且赢的钱数一定要大于已经输了的钱数之和可以把每一场都当作会赢来下注y,必满足w>sum,输的话有sum+=y,这时考虑y最小为(sum/(y乘(k-1))+1),同时,若某一轮sum>a,表示此时硬币数以及<0,说明无法实现,若x+1轮后sum<=a,因为至少应一次的缘故,硬币数>0,说明可以实现。

解释得已经很意识流了,肝了一上午,请大佬轻喷,后续我也会好好改的

代码

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
	int t,a,k,x,ans=0;cin>>t;
	while(t--){
		cin>>k>>x>>a;
		int sum=0;
		for(int i=0;i<=x;i++){
			if(sum>a){
				break;
			} 
			sum+=sum/(k-1)+1;
		} 
		if(sum<=a) cout<<"Yes";
		else cout<<"No";
		cout<<endl;
	}
	return 0;
}

posted @ 2024-02-16 09:16  mono_4  阅读(49)  评论(0编辑  收藏  举报