AtCoder Beginner Contest 292 题解(A-Ex)

AtCoder Beginner Contest 292 题解(A-Ex)

A - CAPS LOCK (Diff. 7)

简要题意:

给你一个只包含小写字母的字符串,将其中每一个字母改为对应的大写字母。

Sample:

Input:

abc

Output:

ABC

Solution:

考察字符串输入输出和 ASCII 码。

对于每一个字符,\(ch\) ,改为 \(ch-'a'+'A'\) 输出即可。

Code:

#include<bits/stdc++.h>
using namespace std;
signed main(){
	string s;
	cin>>s;
	for(int i=0;i<s.size();i++) s[i]=s[i]-'a'+'A';
	cout<<s;
}

B - Yellow and Red Card (Diff. 39)

简要题意:

\(n\) 个足球运动员,有 \(q\) 个询问,每个询问中会有一个运动员染黄或染红,或者查询给定的运动员是否被罚出场。(足球规则:两黄变一红,染红直接被罚出场)

输入中,第一行两个整数,分别表示 \(n,q\)

第二行到第 \((q+1)\) 行,每行两个整数 \(op,x\)

如果 \(op=1\) ,表示球员 \(x\) 领到黄牌。

如果 \(op=2\) ,表示球员 \(x\) 领到红牌。

如果 \(op=3\) ,表示询问球员 \(x\) ,是否被罚出场。

Sample:

Input:

3 9
3 1
3 2
1 2
2 1
3 1
3 2
1 2
3 2
3 3

Output:

No
No
Yes
No
Yes
No

Solution:

按照要求模拟即可,或者用些 trick ,比如可以设黄牌为一分,红牌为两分,满两分出场,记录每个球员的分数即可。

Code:

#include<bits/stdc++.h>
using namespace std;
int a[200010];
signed main(){
	int n, q;
	cin>>n>>q;
	while(q--){
		int op,x;
		cin>>op>>x;
		if(op==1){
			a[x]++;
		}
		if(op==2) a[x]+=2;
		if(op==3){
			if(a[x]>=2){
				cout<<"Yes"<<endl;
			}else{
				cout<<"No"<<endl;
			}
		}
	}
}

C - Four Variable (Diff.444)

简要题意:

给你一个正整数 \(n\) ,问有多少中不同的四元组 \((A,B,C,D)\) 满足 \(AB+CD=n\)

Sample:

Input:

4

Output:

8

$ (A,B,C,D) $ として以下の $ 8 $ 個が考えられます。

  • $ (A,B,C,D)=(1,1,1,3) $
  • $ (A,B,C,D)=(1,1,3,1) $
  • $ (A,B,C,D)=(1,2,1,2) $
  • $ (A,B,C,D)=(1,2,2,1) $
  • $ (A,B,C,D)=(1,3,1,1) $
  • $(A,B,C,D)=(2,1,1,2) $
  • $ (A,B,C,D)=(2,1,2,1) $
  • $ (A,B,C,D)=(3,1,1,1) $

Solution:

考虑枚举 \(AB\) 的乘积,设 \(AB\) 的乘积为 \(i\) 则有:

\[ans_n=\sum\limits_{i=1}^{n-1} dp[i]*dp[n-i] \]

\(dp[i]\) 表示两个正整数乘积为 \(i\) 的方案数。

考虑如何求 \(\{dp\}\) 数组,只需要暴力统计每个数的因数个数就可以了。

时间复杂度在不优化的情况下是 \(O\sqrt{n}\) 的。

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[200010];
int dp[200010];
signed main(){
	for(int i=1;i<=200000;i++){
		for(int j=1;i*j<=200000;j++){
			dp[i*j]++;
		}
	}
	int n;
	cin>>n;
	int ans=0; 
	for(int i=1;i<n;i++){
		ans=ans+dp[i]*dp[n-i];
	}
	cout<<ans<<endl;
}

D - Unicyclic Components(Diff.579)

简要题意:

给你一个无向图,判断是否满足其中每一个联通块的点数和边数均相等。

Sample:

Input:

5 5
1 2
2 3
3 4
3 5
1 5

Output:

Yes

图长这样,显然满足。

Solution:

用并查姬维护联通块,同时维护一下连通块内的点数边数即可。

只需要在合并的时候把信息都存在编号小的点上就好了。

并查姬板子。

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int fa[201000];
int sz[201000];
int nd[200010];
int find(int x){
	if(fa[x]==x) return x;
	return fa[x]=find(fa[x]); 
}
signed main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		fa[i]=i;
		nd[i]=1;
	}
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		u=find(u);
		v=find(v);
		if(u>v) swap(u,v);
		if(u==v){
			sz[u]++;
		} else{
			fa[v]=u;
			nd[u]+=nd[v];
			sz[u]+=sz[v];
			sz[u]++;
		}
	}
	bool tg=false;
	for(int i=1;i<=n;i++){
		if(fa[i]!=i) continue;
		if(sz[i]!=nd[i]){
			tg=true;
			break;
		}
	}
	if(tg==false) cout<<"Yes";
	else cout<<"No";
}

下半篇章。

E - Transitivity (Diff.1,272)

简要题意:

给你一个有向图,问你至少在图中加多少条有向边,才能使图中任意满足 \(A\)\(B\)\(B\)\(C\) 都有直接边的点对 \((A,B,C)\) 均满足 \(A\)\(C\) 也有之间边。

Sample:

Input:

4 3
2 4
3 1
4 3

Output:

3

增加的边:\(2 \rightarrow 3,2 \rightarrow 1,4\rightarrow 1\)

Solution:

考虑样例的情况。

点二与点四有边,点四与点三有边,所以点二连点三,之后类似的,点二也要连点一。

可以推广得到一个点通过路径可以到的所有点都要与它连边(逐步考虑可知)。

所以只需要统计每个点走路径能走到几个点就好了。

Code:

#include<bits/stdc++.h>
using namespace std;
vector<int> g[2010];
bool isv[2010];
int sum;
void dfs(int u,int fa){
	isv[u]=true;
	sum++;
	for(auto x:g[u]){
		if(isv[x]==true) continue;
		dfs(x,u);
	}
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int u,v;
		cin>>u>>v;
		g[u].push_back(v);
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		sum=0;
		for(int j=1;j<=n;j++) isv[j]=false;
		dfs(i,0);
		ans+=sum-1-g[i].size();
	}
	cout<<ans<<endl;
	return 0;
}

F - Regular Triangle Inside a Rectangle (Diff. 1,522)

简要题意:

给定一个矩形的长和宽,求这个矩形内最大正三角形的边长。

Sample:

Input:

1 1

Output:

1.03527618041008295791

构造方案如下,输出即为 \(\sqrt{6}-\sqrt{2}\) 的值。

2cd44ddc3d8241e510a356be9533631f.png (500×500) (atcoder.jp)

Solution:

这题很有意思。

考虑最大的三正角形,发现它一定有一个点在矩形的顶点上。否则,把它平移到顶点上也能成立,所以在顶点上是不劣的。

我们猜测最大的正三角形和图中差不多,另外两个点分别在第一个顶点的对边上。(例如下图)

存在一个 \(\triangle BFG\) 满足条件。

\(BF\) 重点为 \(E\) ,连接 \(GE\)

由三线合一,\(GE \perp BF\) ,又由角 \(ADG=90 \degree\) ,所以 \(FEDG\) 四点共圆。

所以 \(\angle FDG= \angle EFG= 60\degree\) ,同理 $\angle ECD=60 \degree $ 。

所以 \(\triangle DEC\) 是正三角形,即 $E $ 是定点。

可以通过找出矩形的 \(E\) 点,找到最大的三角形。

出现一个问题,就是在 \(E\) 要在正方形内。所以要判断一下。

怎么判断呢?做 \(EH \perp DC\) ,则有 \(EH=\frac{\sqrt{3}}{2} DC\) ,相当于只需要比较长和宽的比值即可。

如果 $E $ 不在正方形呢,那么找类似图二的三角形即为最大的。

即可。

Code:

#include<bits/stdc++.h>
using namespace std;
long double sq3=1.732050807568877;
int main(){
	long double a,b;
	cin>>a>>b;
	if(a>b) swap(a,b);
	if(a/sq3*2<b){
		cout<<fixed<<setprecision(11)<<a*1.000/sq3*2;
		return 0;
	}
	long double x=a/2.0000000;
	long double y=a/2.0000000*sq3;
	long double dlt=2*y-b;
	long double ans=(b-dlt)*(b-dlt)+a*a;
	ans=sqrt(ans);
	cout<<fixed<<setprecision(11)<<ans;
	return 0;
}

G - Count Strictly Increasing Sequences (Diff. 2,340)

简要题意

你有 \(n\) 个数,每个数长度为 \(m\)

不过这 \(n\) 个数中,可能有某些位不确定,需要你在每个 ? 位置上 \(0\)\(9\) 之间填一个数。设你填出来的序列是 \(\{S_i\}\)

请你求出,在所有可能的填数方案中,有多少种满足 \(S_1 < S_2 < \dots < S_n\)?答案对 \(998244353\) 取模。你构造的 \(\{S_i\}\) 允许前导零存在。

数据范围:\(n,m \le 40\)

输入格式

一行两个整数,\(n,m\)

接下来 \(n\) 行,,每行一个长度为 \(m\) 的字符串,表示第 \(i\) 个数。

输出格式

一行一个整数,表示方案个数,答案对 \(998,244,353\) 取模。

Sample:

Input:

3 2
?0
??
05

Output:

4

样例解释:可能的填数方案为:

\((00,01,05),(00,02,05),(00,03,05),(00,04,05)\)

Solution:

很直观的想法是数位 DP ,考虑每一位的填数性质按位做,但发现不太好转移。

考虑再加维度,(毕竟 \(n,m\) 很小)在状态定义中加入当前考虑的区间。

设计状态 \(dp[now][l][r][st]\) ,表示当前考虑数的第 \(i\) 位,当前位的值大于等于 \(st\) ,在考虑第 \(l\) 到第 \(r\) 个数。

显然目标状态为 \(dp[1][1][n][0]\)

考虑状态转移,在区间 \([l,r]\) 中,存在一个断点 \(k\) ,将区间分成两部分。对于 \([l,k]\) ,我们让它们当前考虑的这一位 \(now\) 的值相同,均为 \(st\) 。接下来,我们把排序目标转移到下一位继续 \(dp\)

对于 \([k+1,r]\) ,则它们当前位置已经比区间 \([l,k]\) 的数大了,相当于当前位 \(now\) 的值要大于等于 \(st+1\) ,继续 \(dp\) 即可。此时我们把区间割成了两部分,互不相干,满足区间 \(dp\) 的性质。

状态转移方程:

\[dp[now][l][r][st]=(dp[now][l][r][st+1]+\sum\limits_{k\in [l,r] \&\& (s[k]=='?'||s[k]==st)}dp[now+1][l][k][0]\times dp[now][k+1][r][st+1]) \]

用记忆化搜索写,比较方便。

Code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=998244353;
char a[45][45];
int dp[45][45][45][15];
int n,m;
int dfs(int now,int l,int r,int st){
	if(now>m){
		return (l==r);
	}
	if(l>r){
		return 1;
	}
	if(st>9){
		return 0;
	}
	if(dp[now][l][r][st]>=0) return dp[now][l][r][st];
	int ans=dfs(now,l,r,st+1);
	for(int i=l;i<=r;i++){
		if(a[i][now]!='?'&&a[i][now]!=st+'0') break;
		ans=(ans+dfs(now+1,l,i,0)*dfs(now,i+1,r,st+1)%mod)%mod;
	}
	dp[now][l][r][st]=ans;
	return ans;
}
signed main(){

	cin>>n>>m;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++) cin>>a[i][j];
	}
	memset(dp,-1,sizeof dp);
	cout<<dfs(1,1,n,0)<<endl;
	return 0;
}

Ex - Rating Estimator(Diff. 2,248)

简要题意

给定一个人 \(n\) 场比赛的表现分 \(a_i\) ,在第 \(k\) 场比赛后 \((1 \le k \le n)\) ,他的 \(rating\) 变化为 \(\frac{1}{n} \sum\limits_{1 \le j \le k} a_j\)

但是当一个人的 \(rating\) 达到 \(B\) 的时候,它的 \(rating\) 就不再变化了。

给定 \(q\) 个询问,每次询问给定两个数 \(a_i\)\(x_i\) ,表示这个人第 \(a_i\) 场比赛的表现分变成 \(x_i\) ,求修改后他最终的 \(rating\) ,操作可持久化。

\(n \le 5\times 10^5,q \le 10^5\)

Sample:

Input:

5 6 7
5 1 9 3 8
4 9
2 10
1 0
3 0
3 30
5 100
1 100

Output:

6.000000000000000
7.500000000000000
6.333333333333333
5.400000000000000
13.333333333333334
13.333333333333334
100.000000000000000

Solution:

先推一下式子,考虑到什么时候 \(rating\) 才爆分。

\[\begin{aligned} &\frac{1}{k}\sum\limits_{1\le i \le k} a_i > B \\ \implies &\sum\limits_{1\le i \le k} a_i > Bk \\ \implies & \sum\limits_{1\le i \le k} (a_i-B)> 0\\ \end{aligned} \]

考虑构造一个数组 \(\{b_n\}\) ,满足 \(b_i=a_i-B(1\le i \le n)\) ,那么当 \(b_i\) 的前缀和第一次大于 \(0\) 时完成爆分。

直接用线段树维护区间里前缀和的最大值。

考虑查询的时候要线段树上二分找到第一次爆分,所以还要维护区间和。

线段树上二分的一些细节见代码。

Code:

#include<bits/stdc++.h>
#define int long long
using namespace std;
struct tree{
	int id,l,r,mx,sum,lazy;
}t[2000010];
int a[500010];
void pushup(int id){
	t[id].sum=t[id<<1].sum+t[id<<1|1].sum;
	t[id].mx=max(t[id<<1].mx,t[id<<1].sum+t[id<<1|1].mx);
}
void pushdown(int id){
	if(t[id].lazy!=0){
		t[id<<1].lazy=t[id<<1|1].lazy=t[id].lazy;
		t[id<<1].sum=t[id].lazy*(t[id<<1].r-t[id<<1].l+1);
		t[id<<1|1].sum=t[id].lazy*(t[id<<1|1].r-t[id<<1|1].l+1);
		t[id].lazy=0;
	}
}
void build(int id,int l,int r){
	t[id].l=l;
	t[id].r=r;
	t[id].sum=t[id].lazy=t[id].mx=0;
	if(l==r){
		t[id].sum=a[l];
		t[id].mx=a[l];
		return;
	}
	int  mid=(l+r)>>1;
	build(id<<1,l,mid);
	build(id<<1|1,mid+1,r);
	pushup(id);
}
void update(int id,int l,int r,int v){

	if(l<=t[id].l&&t[id].r<=r){
		t[id].sum=v*(t[id].r-t[id].l+1);
		t[id].lazy=v;
		t[id].mx=v*(t[id].r-t[id].l+1);
		return;
	}
	pushdown(id);
	int mid=(t[id].l+t[id].r)>>1;
	if(l<=mid) update(id<<1,l,r,v);
	if(r>mid) update(id<<1|1,l,r,v);
	pushup(id);
}
pair<int,int> query(int id,int l,int r,int sum){
//	cout<<id<<" "<<l<<" "<<r<<" "<<sum<<" "<<t[id].sum<<" "<<t[id<<1].mx<<endl;
	if(t[id].l==t[id].r){
		return {t[id].l,sum+t[id].mx};
	}
	pushdown(id);
	if(sum+t[id<<1].mx>=0){
		return query(id<<1,l,r,sum);
	}else{
		return query(id<<1|1,l,r,sum+t[id<<1].sum);
	}
}
signed main(){
	int n,b,q;
	cin>>n>>b>>q;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		a[i]-=b;
	}
	build(1,1,n);
	while(q--){
		int op,x;
		cin>>op>>x;
		x-=b;
		update(1,op,op,x);
	//	return 0;
		pair<int,int> pr=query(1,1,n,0);
	//	cout<<pr.first<<" "<<pr.second<<endl;
		double ans=b+1.000000*pr.second/pr.first;
		cout<<fixed<<setprecision(10)<<ans<<endl;
	}
} 
posted @ 2023-03-21 23:04  ArizonaYYDS  阅读(112)  评论(0)    收藏  举报