【题解】CF2 合集

前言:

  1. 本人不会 LaTeX……请见谅
  2. 码风奇特,不喜勿喷哈
  3. 题面翻译取自 luogu,本蒟蒻也会安置原题链接
  4. 保证文章中不出现“显然”或者“注意到”,可能会出现“易证”
  5. AC 代码会放置在每一个题目的最底端,为防止 ban 码的情况出现,不设置跳转链接
  6. 有写错的地方欢迎各位神犇指正
  7. 本套题共 3 道,预计阅读 + 理解时间小于 40min

正片开始!

CF2A

题面(可从下方链接跳转看原题题面):

在 Berland 流行着纸牌游戏 Berlogging,这个游戏的赢家是根据以下规则确定的:

在每一轮中,玩家获得或失去一定数量的分数,在游戏过程中,分数被记录在 名称和得分 行中,其中名字是玩家的名字,得分是在这一轮中获得的分数。得分是负值意味着玩家失去了相应的分数。
如果在比赛结束时只有一名玩家分数最多,他就是获胜者
如果两名或两名以上的玩家在比赛结束时都有最大的分数 m,那么其中首先获得至少 m 分的玩家胜利。开始时,每个玩家都是 0 分。
保证在比赛结束时至少有一个玩家的分数为正

题目传送门

序言 & 结论:

难度:黄题

map 映射的熟练应用是解决此题的必要条件

推理过程:

  • 对于每一个玩家,他在任意一个时刻会对应一个分数,这样的对应关系看可以考虑 map
  • 可以令 map 的类型为 <string,int>,表示从名字到分数的一个映射
  • 那么,对于第一种情况,直接做做完了
  • 对于第二种情况,我们考虑先预处理出分数最大值,然后重新模拟加分过程
  • 只要遇到合法的情况就输出并退出

细节处理:

  • 存在负数,mx 的初始化要设为 -inf
  • 其次注意到第二次模拟加分过程,我们考虑判断条件应设为 mp[name]>=mx (虽然 mx 是最大值,但不一定是全过程的最大值)
  • 结构体也是可做的~

代码:

干就完了!

点击查看代码
#include<iostream>
#include<map>
using namespace std;
const int maxn=1e3+10;
map<string,int> mp1,mp2;
int n,a[maxn],mx=-1;
string s[maxn],res;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
    for(int i=1;i<=n;i++){
        cin>>s[i]>>a[i];
        mp1[s[i]]+=a[i];
    }
    for(int i=1;i<=n;i++){
        mx=max(mx,mp1[s[i]]);
    }
    for(int i=1;i<=n;i++){
        mp2[s[i]]+=a[i];
        if(mp1[s[i]]==mx&&mp2[s[i]]>=mx){
            res=s[i];
            break;
        }
    }
    cout<<res<<endl;
    return 0;
}

----------------------不 ~ 开 ~ 心 ~ 的分割线----------------------

CF2B

题面(可从下方链接跳转看原题题面):

给定由非负整数组成的 n×n 的正方形矩阵,你需要寻找一条路径:

  1. 以左上角为起点
  2. 每次只能向右或向下走
  3. 以右下角为终点
  4. 如果我们把沿路遇到的数进行相乘,积应当以尽可能少的 0 结尾

题目传送门

序言 & 结论:

本以为是个普通 dp,没曾想还给我整这死出
题面只有条件4是变种,其他都是板子

难度:绿题

推理过程:

  • 如果不加入条件4,那就是一个 dp 的板子题,转移方程形如:
    dp[i][j]=dp[i-1][j]+dp[i][j-1]
  • 那么,我们在这个板子的基础上处理条件4
  • 首先,考虑转化一下“以尽可能少的 0 结尾”
  • 由题意,答案是由路径上的乘积构成的……
  • 末尾出现 0,说明因子必含有 0/10
  • 如若含 0,乘积是 0,末尾有一个 0
  • 对于不含 0 的情况,若乘积能分出 k 个 10 末尾就有 k 个 0
  • 所以直接判断有几个数是 10 的倍数即可——吗?
  • 考虑一条路径,形如:
  • 我们发现,乘积是 9000,末尾有三个 0,但是该序列不存在 10 的倍数
  • 可以发现,10=2×5
  • 因此,我们不能去记录 10 的倍数,而是记录每个数对于 2 和 5 的贡献
  • 在代码实现上,可以考虑将状态设置成 dp[i][j][0/1]
  • 第一问做完了,进入第二问——打印路径
  • 我们在 dp 过程中进行判断,贪心没有后效性
  • 比较当前状态与上一步的状态,若对 2 和 5 贡献少的就记录方向
  • 最后打印路径

细节处理:

  • 0 的特判(如果矩阵中存在 0,则需要特殊标记)
  • 2 和 5 要分开处理,两者互不干扰

代码:

代码缩进可能出现一部分神秘问题,不喜勿喷哈!

点击查看代码
#include<iostream>
#include<vector>
using namespace std;
const int maxn=1024;
int n;
int dp[maxn][maxn][2];
char dir[maxn][maxn][2];
vector<char> ans;
int main() {
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
    cin>>n;
    bool flag=false;
    int zx,zy;
    for(int i=1;i<=n;i++){
    	for(int j=1;j<=n;j++){
    		int x;
    		cin>>x;
    		if(!x){
    			flag=true;
    			zx=i;
    			zy=j;
    			continue;
			}
			while(x%2==0){
                dp[i][j][0]++;
                x/=2;
            }
            while(x%5==0){
                dp[i][j][1]++;
                x/=5;
            }
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			if(i==1&&j==1){
				continue;
			}else if(i>1&&j>1){
                if(dp[i-1][j][0]>dp[i][j-1][0]){
                	dir[i][j][0]='R';
					dp[i][j][0]+=dp[i][j-1][0];
				}else{
					dir[i][j][0]='D';
					dp[i][j][0]+=dp[i-1][j][0];
				}
                if(dp[i-1][j][1]>dp[i][j-1][1]){
                	dir[i][j][1]='R';
					dp[i][j][1]+=dp[i][j-1][1];
				}else{
					dir[i][j][1]='D';
					dp[i][j][1]+=dp[i-1][j][1];
				}
            }else if(i>1&&j==1){
                dir[i][j][0]=dir[i][j][1]='D';
				dp[i][j][0]+=dp[i-1][j][0];
				dp[i][j][1]+=dp[i-1][j][1];
            }else if(j>1&&i==1){
            	dir[i][j][0]=dir[i][j][1]='R';
				dp[i][j][0]+=dp[i][j-1][0];
				dp[i][j][1]+=dp[i][j-1][1];
			}                
		}
	}
	int d=(dp[n][n][1]<dp[n][n][0]?1:0);
    if(flag&&dp[n][n][d]>0){
    	cout<<1<<endl;
    	for(int i=1;i<zx;i++){
    		cout<<'D';
		}
		for(int i=1;i<zy;i++){
    		cout<<'R';
		}
		for(int i=zx+1;i<=n;i++){
    		cout<<'D';
		}
		for(int i=zy+1;i<=n;i++){
    		cout<<'R';
		}
        return 0;
    }
    cout<<dp[n][n][d]<<endl;
    int x=n,y=n;
    vector<char> ans;
    while(x!=1||y!=1){
    	ans.push_back(dir[x][y][d]);
    	if(dir[x][y][d]=='D'){
    		x--;
		}else{
			y--;
		}
	}
    for(int i=ans.size()-1;i>=0;i--){
    	cout<<ans[i];
	}
	return 0;
}

----------------------不 ~ 开 ~ 心 ~ 的分割线----------------------

CF2C

题面(可从下方链接跳转看原题题面):

给定三个圆,找到一个点,使得到三个圆的两切线张角相同,取最大张角的那个点输出,没有则不输出

题目传送门

序言 & 结论:

我讨厌计(或者叫解析几何?)

需要很多的数学知识(甚至本题我没有用到任何的 OI 知识)

所以直接略过看代码吧!

推理过程:

细节处理:

  • 不要开 long double,直接寄
  • 建议输入输出选用 scanf,printf
  • 公式的计算可以现在草纸上书写一遍,再敲代码(这是一个悲伤的故事~)

代码:

其实这种题目直接拿代码走人吧!

本蒟蒻自己写完的代码自己都看不懂了(得亏有部分注释)

点击查看代码
#include<iostream>
#include<cstdio>
#include<cmath> 
using namespace std;
const double eps=1e-6;
//点 
struct node{
	double x,y;
};
//向量 
struct vect{
	double x,y;
};
//圆 
struct circ{
	double x,y,r;
};
inline double node_dis(node X,node Y){
	double dx=X.x-Y.x,dy=X.y-Y.y;
	return sqrt(dx*dx+dy*dy);
}
inline double vect_len(vect X){
	return sqrt(X.x*X.x+X.y*X.y);
}
int main(){
	double r1,r2,r3;
	node A,B,C,M,N;
	vect u,v;
	scanf("%lf%lf%lf",&A.x,&A.y,&r1);
	scanf("%lf%lf%lf",&B.x,&B.y,&r2);
	scanf("%lf%lf%lf",&C.x,&C.y,&r3);
	//向量初始化 
	u=(vect){B.x-A.x,B.y-A.y};
	v=(vect){C.x-A.x,C.y-A.y};
	//直线与直线 
	if(r1==r2&&r1==r3){
		//两直线平行 
		if(u.x*v.y==v.x*u.y){
			return 0;
		}
		//垂直 
		vect du=(vect){-u.y,u.x};
		vect dv=(vect){-v.y,v.x};
		M=(node){A.x+u.x*0.5,A.y+u.y*0.5};
		N=(node){A.x+v.x*0.5,A.y+v.y*0.5};
		double t1;
		//解方程 
		t1=(N.x*dv.y-N.y*dv.x+dv.x*M.y-dv.y*M.x)/(du.x*dv.y-du.y*dv.x);
		printf("%.5f %.5f\n",M.x+du.x*t1,M.y+du.y*t1);
	}
	//直线与圆 
	if(r1==r2&&r1!=r3){
		vect _u=(vect){-u.y,u.x};
		M=(node){A.x+u.x*0.5,A.y+u.y*0.5};
		//求圆与对称轴的交点
		node P=(node){A.x+v.x*(r1/(r1+r3)),A.y+v.y*(r1/(r1+r3))};
		node Q=(node){C.x+v.x*(r3/(r1-r3)),C.y+v.y*(r3/(r1-r3))};
		//阿氏圆 
		circ N=(circ){(P.x+Q.x)*0.5,(P.y+Q.y)*0.5,node_dis(P,Q)*0.5};
		//向量计算 
		vect MN=(vect){N.x-M.x,N.y-M.y};
		double dMN=vect_len(MN),d_u=vect_len(_u);
		double ddu=(_u.x*MN.x+_u.y*MN.y)/d_u;
		if(ddu*ddu+N.r*N.r<dMN*dMN){
			return 0;
		}
		double d=sqrt(dMN*dMN-ddu*ddu);
		double dd1=ddu-sqrt(N.r*N.r-d*d);
		double dd2=ddu+sqrt(N.r*N.r-d*d);
		//反手一个解得 
		node T1=(node){M.x+_u.x*dd1/d_u,M.y+_u.y*dd1/d_u};
		node T2=(node){M.x+_u.x*dd2/d_u,M.y+_u.y*dd2/d_u};
		if(node_dis(T1,A)<node_dis(T2,A)){
			printf("%.5f %.5f\n",T1.x,T1.y);
		}else{
			printf("%.5f %.5f\n",T2.x,T2.y);
		}
	}
	//圆与直线 
	if(r1!=r2&&r1==r3){
		vect _v=(vect){-v.y,v.x};
		N=(node){A.x+v.x*0.5,A.y+v.y*0.5};
		node P=(node){A.x+u.x*(r1/(r1+r2)),A.y+u.y*(r1/(r1+r2))};
		node Q=(node){B.x+u.x*(r2/(r1-r2)),B.y+u.y*(r2/(r1-r2))};
		circ M=(circ){(P.x+Q.x)*0.5,(P.y+Q.y)*0.5,node_dis(P,Q)*0.5};
		vect NM=(vect){M.x-N.x,M.y-N.y};
		double dNM=vect_len(NM),d_v=vect_len(_v);
		double ddv=(_v.x*NM.x+_v.y*NM.y)/d_v;
		if(ddv*ddv+M.r*M.r<dNM*dNM){
			return 0;
		}
		double d=sqrt(dNM*dNM-ddv*ddv);
		double dd1=ddv-sqrt(M.r*M.r-d*d);
		double dd2=ddv+sqrt(M.r*M.r-d*d);
		node T1=(node){N.x+_v.x*dd1/d_v,N.y+_v.y*dd1/d_v};
		node T2=(node){N.x+_v.x*dd2/d_v,N.y+_v.y*dd2/d_v};
		if(node_dis(T1,A)<node_dis(T2,A)){
			printf("%.5f %.5f\n",T1.x,T1.y);
		}else{
			printf("%.5f %.5f\n",T2.x,T2.y);
		}
	}
	//圆与圆 
	if(r1!=r2&&r1!=r3){
		node P,Q;
		P=(node){A.x+u.x*(r1/(r1+r2)),A.y+u.y*(r1/(r1+r2))};
		Q=(node){B.x+u.x*(r2/(r1-r2)),B.y+u.y*(r2/(r1-r2))};
		circ M=(circ){(P.x+Q.x)*0.5,(P.y+Q.y)*0.5,node_dis(P,Q)*0.5};
		P=(node){A.x+v.x*(r1/(r1+r3)),A.y+v.y*(r1/(r1+r3))};
		Q=(node){C.x+v.x*(r3/(r1-r3)),C.y+v.y*(r3/(r1-r3))};
		circ N=(circ){(P.x+Q.x)*0.5,(P.y+Q.y)*0.5,node_dis(P, Q)*0.5 };
		double d=node_dis((node){M.x,M.y},(node){N.x,N.y});
		//两圆check交点 
		if(d>=M.r+N.r){
			return 0;
		}
		if(d+M.r<=N.r){
			return 0;
		} 
		if(d+N.r<=M.r){
			return 0;
		} 
		if(abs(M.x-N.x)<eps&&abs(M.y-N.y)<eps){
			return 0;
		}
		//余弦定理求角 
		double alpha=acos((d*d+M.r*M.r-N.r*N.r)/(d*M.r*2.0));
		double cosr=M.r*cos(alpha);
		double sinr=M.r*sin(alpha);
		//向量,还是向量! 
		vect MN=(vect){N.x-M.x,N.y-M.y};
		node S=(node){M.x+MN.x*cosr/vect_len(MN),M.y+MN.y*cosr/vect_len(MN)};
		vect _MN=(vect){-MN.y,MN.x};
		node T1=(node){S.x+_MN.x*sinr/vect_len(_MN),S.y+_MN.y*sinr/vect_len(_MN)};
		node T2=(node){S.x-_MN.x*sinr/vect_len(_MN),S.y-_MN.y*sinr/vect_len(_MN)};
		if(node_dis(T1,A)<node_dis(T2,A)){
			printf("%.5f %.5f\n",T1.x,T1.y);
		}else{
			printf("%.5f %.5f\n",T2.x,T2.y);
		} 
	}
	return 0;
}

完结撒花!

posted @   sunxuhetai  阅读(188)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示