Atcoder Beginner Contest 266题目分析

本文提供了AtCoder Beginner Contest 266的详细题解,覆盖了从字符串操作到动态规划、概率论及图论等多个算法领域的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Atcoder Beginner Contest 266题目分析

A - Middle Letter

计算出来字符串的长度,因为是长度为奇数的字符串,输出下标为 ( l e n + 1 ) / 2 (len+1)/2 (len+1)/2的字符即可(这里字符串下标从1开始)

#include<bits/stdc++.h>
using namespace std;
char a[110];
int main(){
	cin>>a+1;
	int len=strlen(a+1);
	cout<<a[(len+1)/2];
	
}
B - Modulo Number

枚举 998244353 998244353 998244353的倍数 k k k x = N − 998244353 ∗ k x=N-998244353*k x=N998244353k,判断 x x x是否在 [ 0 , 998244353 − 1 ] [0,998244353-1] [0,9982443531]之间,如果是则输出 x x x,如果不是则继续。并且我们可以通过 x x x的范围去计算出 k k k的范围,从而减少枚举次数。

#include<bits/stdc++.h>
using namespace std;
const long long y= 998244353;
int main(){
	long long n;
	cin>>n;
	long long l=(n-998244352)/y-1;
	long long r=n/y+1;
	for(long long i=l;i<=r;i++){
		long long x=n-y*i;
		if(x>=0&&x<=998244352){
			cout<<x;
			break;
		}
	}

}
C - Convex Quadrilateral

题目大意,给你一个四边形,请你判断它是不是四个内角都小于180°,如果是则输出 Y e s Yes Yes,否则输出 N o No No

判断四边形内角跟180°的大小,我们可以使用叉积来计算。如果两个向量之间的叉积是小于0的则说明该内角是大于180°的。

那么到底什么是叉积呢?

在二维向量内,两个向量的叉积相当于两个向量围成的面积,即 v ⃗ × w ⃗ = ∣ v ∣ ∣ w ∣ s i n θ \vec{v} \times \vec{w} =|v| |w|sin\theta v ×w =v∣∣wsinθ

注意:这里的角 θ \theta θ v ⃗ \vec{v} v w ⃗ \vec{w} w 逆时针的夹角

在这里插入图片描述

所以在本题中我们就可以通过两个向量的叉积去判断它们的夹角是否大于180°

例如:

Figure

C D ⃗ × C B ⃗ = ∣ C D ⃗ ∣ ∣ C B ∣ ⃗ s i n θ \vec{CD}\times \vec{CB} =|\vec{CD}||\vec{CB|} sin\theta CD ×CB =CD ∣∣CB sinθ,为什么这里是 C D ⃗ × C B ⃗ \vec{CD}\times \vec{CB} CD ×CB 是因为夹角 θ \theta θ是指的从第一个向量出发,逆时针到第二个向量之间的夹角。

在坐标系中向量 C D ⃗ \vec{CD} CD 的坐标可以表示成 ( x d − x c , y d − y c ) (x_d-x_c,y_d-y_c) (xdxc,ydyc),所以本题我们只需要注意方向,使得夹角都是内角即可。

#include<bits/stdc++.h>
using namespace std;
int cau(int a1,int b1,int a2,int b2){
	return a1*b2-b1*a2;
}
int main(){
	int x1,y1,x2,y2,x3,y3,x4,y4;
	cin>>x1>>y1>>x2>>y2>>x3>>y3>>x4>>y4;
	if(cau(x2-x1,y2-y1,x4-x1,y4-y1)<0){
		cout<<"No"<<endl;
		return 0;
	}
	if(cau(x3-x2,y3-y2,x1-x2,y1-y2)<0){
		cout<<"No"<<endl;
		return 0;
	}
	if(cau(x4-x3,y4-y3,x2-x3,y2-y3)<0){
		cout<<"No"<<endl;
		return 0;
	}
	if(cau(x1-x4,y1-y4,x3-x4,y3-y4)<0){
		cout<<"No"<<endl;
		return 0;
	}

cout<<"Yes"<<endl;
}

D - Convex Quadrilateral

该题目很明显的具有动态规划的特征。1.他是每秒钟移动一格,可以按照时间划分问题的阶段 2.我们可以用 d p [ t ] [ p o s ] dp[t][pos] dp[t][pos]去表示Takahashi的状态,即在 t t t这个时间点Takahashi所在的位置。3.Takahashi的在时间 t t t的状态可以由在 t − 1 t-1 t1的时间转化出来,即 d p [ t ] [ p o s ] = m a x ( d p [ t − 1 ] [ p o s − 1 ] , d p , m a x ( d p [ t − 1 ] [ p o s ] , d p [ t − 1 ] [ p o s + 1 ] ) ) + v a l u e [ p o s ] dp[t][pos]=max(dp[t-1][pos-1],dp,max(dp[t-1][pos],dp[t-1][pos+1]))+value[pos] dp[t][pos]=max(dp[t1][pos1],dp,max(dp[t1][pos],dp[t1][pos+1]))+value[pos]

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int a[N];
int pos[N];
long long dp[N][6];
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		int t1,x,a1;
		cin>>t1>>x>>a1;
		pos[t1]=x;
		a[t1]=a1;
	}
	for(int i=1;i<5;i++) dp[0][i]=-1e18;
	for(int i=1;i<=1e5;i++){
		for(int j=0;j<5;j++){
			dp[i][j]=dp[i-1][j];
			if(j!=0) dp[i][j]=max(dp[i][j],dp[i-1][j-1]);
			if(j!=4) dp[i][j]=max(dp[i][j],dp[i-1][j+1]);
		}
		dp[i][pos[i]]+=a[i];
	}
	long long ans=0;
	for(int i=0;i<5;i++) ans=max(ans,dp[100000][i]);
	cout<<ans;
	
	
}

E - Throwing the Die

期望

在第 n n n次抛掷所取得结果有两种来源,一个是本次抛掷的结果,另一个就是上一次抛掷的结果,我们取两次结果的最大值,因为是骰子所以每种结果的概率都是 1 6 \frac{1}{6} 61。那么 f ( N ) = 1 6 m a x ( 1 , f ( N − 1 ) ) + 1 6 m a x ( 2 , f ( N − 1 ) ) + 1 6 m a x ( 3 , f ( N − 1 ) ) + 1 6 m a x ( 4 , f ( N − 1 ) ) + 1 6 m a x ( 5 , f ( N − 1 ) ) + 1 6 m a x ( 6 , f ( N − 1 ) ) f(N)=\frac{1}{6}max(1,f(N−1))+\frac{1}{6}max(2,f(N−1))+\frac{1}{6}max(3,f(N−1))+\frac{1}{6}max(4,f(N−1))+\frac{1}{6}max(5,f(N−1))+\frac{1}{6}max(6,f(N−1)) f(N)=61max(1,f(N1))+61max(2,f(N1))+61max(3,f(N1))+61max(4,f(N1))+61max(5,f(N1))+61max(6,f(N1))

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	cin>>n;
	double ans=3.5;
	for(int i=1;i<n;i++){
		double ans2=0;
		for(double j=1;j<=6;j++) ans2+=max(j,ans)/6;
		ans=ans2;
	}
	printf("%.10lf",ans);
}

F - Well-defined Path Queries on a Namori

基环树

N N N个顶点 N N N条边组成的无向连通图,显然这个图中肯定有一个环,也就是我们经常说的基环树。题目问两点之间是否存在唯一一条简单路径,我们从下图中可以发现,相同颜色的点之间存在唯一一条的简单路径,但是不同颜色的点之间存在多条简单路径,所以本题的思路就很明确了:1.通过拓扑排序将环上的点找出来进行标记 2.以环上的点为根,用dfs对未被标记的点进行染色。3.判断这两点之间的颜色是否相同,如果相同就说明存在唯一一条简单路径,否则不存在

在这里插入图片描述
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int h[N],e[2*N],ne[2*N],idx=0;
int deg[N];
int mark[N];
queue<int>q;
void add(int x,int y){
	e[++idx]=y;
	ne[idx]=h[x];
	h[x]=idx;
	deg[y]++;
}
void topsort(){

	while(q.size()){
		int x=q.front();
	    q.pop();
	    for(int i=h[x];i;i=ne[i]){
	    	int j=e[i];
	    	deg[j]--;
	    	if(deg[j]==1) q.push(j);
		}
	}
}
void dfs(int x,int fa){
	mark[x]=mark[fa];
	for(int i=h[x];i;i=ne[i]){
		int v=e[i];
		if(v==fa||deg[v]>1) continue;
		dfs(v,x);
	}
}
int main(){
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		int u,v;
		cin>>u>>v;
		add(u,v);
		add(v,u);
	}
	for(int i=1;i<=n;i++){
		if(deg[i]==1) q.push(i);
	}
	topsort();
	for(int i=1;i<=n;i++){
		if(deg[i]>1){
			mark[0]=i;
			dfs(i,0);
		} 
	}
	int q;
	cin>>q;
	while(q--){
		int x,y;
		cin>>x>>y;
		if(mark[x]!=mark[y]) cout<<"No"<<endl;
		else cout<<"Yes"<<endl;
	}
}

G - Yet Another RGB Sequence
题目大意:给你四个整数 R , G , B , K R,G,B,K R,G,B,K,请问有多少个只包含这R G B三个字符并满足下列条件的字符串 S S S?答案取模 998244353 998244353 998244353后输出

  • S中R G B出现的次数分别为R、G、B。
  • RG 作为(连续)子串在 S 中出现的次数为 K。

思路:可以先将RG看作一个整体·K,然后将K个K,G-K个G,B个B进行排列,最后再将R-K个R插入进去,但不能插在G的右边即可

K个K,G-K个G,B个B排列有 ( b + k + g − k ) ! ( g − k ) ! b ! k ! \frac{(b + k + g-k)!}{(g-k)!b!k!} (gk)!b!k!(b+k+gk)!种,接下来将剩下的R插入进去,并且 R R R不能插在 G G G的右边,所以我们能够填 R R R的位置有 ( G − K ) + K + B + 1 − ( G − K ) = B + K + 1 (G-K)+K+B+1-(G-K)=B+K+1 (GK)+K+B+1(GK)=B+K+1

所以总共的次数 ( b + k + g − k ) ! ( g − k ) ! b ! k ! ⋅ ( ( r − k ) + s p o t s − 1 r − k ) \frac{(b + k + g-k)!}{(g-k)!b!k!} \cdot \binom{(r-k) + spots - 1}{r-k} (gk)!b!k!(b+k+gk)!(rk(rk)+spots1)

#include <bits/stdc++.h>

#include <atcoder/modint>
using namespace std;

using mint = atcoder::modint998244353;

mint fac[3000001], finv[3000001], inv[3000001];

void setup() {
    const int MOD = mint::mod();
    fac[0] = fac[1] = 1;
    finv[0] = finv[1] = 1;
    inv[1] = 1;
    for(int i = 2; i <= 3000000; i++) {
        fac[i] = fac[i - 1] * i;
        inv[i] = (mint)MOD - inv[MOD % i] * (MOD / i);
        finv[i] = finv[i - 1] * inv[i];
    }
}

mint binom(int n, int r) {
    if(n < r or n < 0 or r < 0) return mint::raw(0);
    return fac[n] * finv[r] * finv[n - r];
}

int main() {
    setup();
    int r, g, b, k;
    cin >> r >> g >> b >> k;
    r -= k, g -= k;
    mint res = fac[k + g + b] * finv[k] * finv[g] * finv[b];
    res *= fac[k + b + r] * finv[r] * finv[k + b];
    cout << res.val() << endl;
}

有什么不对的地方,恳望大家指出,多多交流,多多点赞嘿嘿

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值