AtCoder Beginner Contest 362 A~E

A.Buy a Pen(思维)

题意:

高桥来到一家商店购买钢笔。在这里,一支红笔的价格是 R R R日元,一支绿笔的价格是 G G G日元,一支蓝笔的价格是 B B B日元。

高桥不喜欢 C C C这种颜色。如果 C C C是"红色",他就不能购买红色钢笔;如果 C C C是"绿色",他就不能购买绿色钢笔;如果 C C C是"蓝色",他就不能购买蓝色钢笔。

求他买一支笔最少需要多少钱?

分析:

分类讨论三种情况,取最小即可。

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=250005;
const int MOD=1000000007;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int r,g,b;
    string c;
    cin>>r>>g>>b>>c;
    if(c[0]=='R') {
        r=66666;
    } 
	else if(c[0]=='G') {
        g=66666;
    } 
	else{
        b=66666;
    }
    cout<<min({r,g,b})<<endl;
 
    return 0;
}

B.Right Triangle(数学)

题意:

x y xy xy平面上,有三个点 A ( x A , y A ) A(x_A,y_A) A(xA,yA) B ( x B , y B ) B(x_B,y_B) B(xB,yB) C ( x C , y C ) C(x_C,y_C) C(xC,yC)不相交。请判断三角形 A B C ABC ABC是否为直角三角形。

分析:

分别算出三条边的长度,使用勾股逆定理判断出三角形是否为直角三角形。

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N=250005;
const int MOD=1000000007;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
    int xA,yA,xB,yB,xC,yC;
    cin>>xA>>yA>>xB>>yB>>xC>>yC;
    int AB=(xB-xA)*(xB-xA)+(yB-yA)*(yB-yA);
    int BC=(xC-xB)*(xC-xB)+(yC-yB)*(yC-yB);
    int CA=(xC-xA)*(xC-xA)+(yC-yA)*(yC-yA);
    if(AB+BC==CA || AB+CA==BC || BC+CA==AB) {
        cout<<"Yes"<<endl;
    } 
    else{
        cout<<"No"<<endl;
    }
    return 0;
}

C.Sum = 0(贪心)

题意:

给你 N N N对整数 ( L 1 , R 1 ) , ( L 2 , R 2 ) , … , ( L N , R N ) (L_1,R_1),(L_2,R_2),\ldots,(L_N,R_N) (L1,R1),(L2,R2),,(LN,RN)

请判断是否存在满足以下条件的由 N N N个整数 X = ( X 1 , X 2 , … , X N ) X=(X_1,X_2,\ldots,X_N) X=(X1,X2,,XN)组成的序列,如果存在,请输出一个这样的序列。

  • L i ≤ X i ≤ R i L_i\leq X_i\leq R_i LiXiRi为每个 i = 1 , 2 , … , N i=1,2,\ldots,N i=1,2,,N
  • ∑ i = 1 N X i = 0 \displaystyle\sum_{i=1}^N X_i=0 i=1NXi=0

分析:

本题我们考虑贪心。可以先得到一个最小值,即所有的左端点相加,记和为 n o w now now,则如果 n o w > 0 now>0 now>0,则不可能构成 0 0 0,输出 N o No No

然后遍历每个区间,尽可能的使 n o w now now更接近 0 0 0,分两种情况讨论。

  • n o w now now变成 0 0 0的距离小于区间大小。这意味着只要加上这个区间的一部分就能使得 n o w now now变为 0 0 0
  • n o w now now变成 0 0 0的距离大于区间大小。这意味着需要加上这个区间的全部。

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int N = 2e5 + 10;
const int INF = 0x3f3f3f3f;
LL n;
LL l[N], r[N];
LL c[N];

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin >> n;
	for(int i = 1; i <= n; i++) 
		cin >> l[i] >> r[i];
	LL now = 0;
	for(int i = 1; i <= n; i++){
		now += l[i];
		c[i] = l[i];
	}
	if(now > 0){
		cout<<"No"<<endl;
		exit(0);
	}
	for(int i = 1; i <= n;i++){
		LL ned = 0 - now;
		if(r[i] - l[i] >= ned){
			c[i] = l[i] + ned;
			cout<<"Yes"<<endl;
			for(int i = 1; i <= n; i++) 
				cout << c[i] << " ";
			cout << endl;
			exit(0);
		}
		c[i] = r[i];
		now += (r[i] - l[i]);
	}
	cout << "No"<<endl; 
	return 0;
}

D.Shortest Path 3(最短路)

题意:

问题陈述

给你一个简单连接的无向图,有 N N N个顶点和 M M M条边。每个顶点 i ( 1 ≤ i ≤ N ) i(1\leq i\leq N) i(1iN)的权重为 A i A_i Ai。每条边 j ( 1 ≤ j ≤ M ) j(1\leq j\leq M) j(1jM)双向连接顶点 U j U_j Uj V j V_j Vj,权重为 B j B_j Bj

该图中路径的权重定义为路径上出现的顶点和边的权重之和。

针对每个 i = 2 , 3 , … , N i=2,3,\dots,N i=2,3,,N求解以下问题:

  • 找出从顶点 1 1 1到顶点 i i i的路径的最小权重。

分析:

本题为最短路模板题。首先建立无向图,然后将从顶点 1 1 1到其他所有点的最短路径权重设为一个很大的值。这里用 d i s dis dis数组来存储顶点 1 1 1到其他所有点的最短路径权重。

接下来我们用 D i j k s t r a Dijkstra Dijkstra算法来求顶点 1 1 1到其他所有点的最短路径权重。这里使用优先队列优化的 D i j k s t r a Dijkstra Dijkstra。优先队列将所有顶点的权重从小到大排序。如果优先队列不为空,取出顶端元素,将当前顶点的权重设为 w w w,将当前顶点设为 u u u。如果 w w w大于目前已知的最短路径权重,也就是 d i s u dis_u disu,那么跳过。

遍历当前顶点的所有邻接顶点,设当前遍历到的邻接顶点是 v v v,计算从顶点 1 1 1到顶点 v v v的路径权重,设这个值为 n x t nxt nxt,如果 n x t nxt nxt小于 d i s v dis_v disv,则将 d i s v dis_v disv更新为 n x t nxt nxt,并且将 n x t nxt nxt v v v加入优先队列。

最后,输出 d i s dis dis数组。

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;
const int INF = 0x3f3f3f3f;
#define pii pair<LL,LL>
#define N 400005
struct Node{
	LL to,far;
};
LL a[N],dis[N];

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
	LL n,m;
	cin>>n>>m;
	vector<vector<Node>>G(n+1);
	for(LL i=1;i<=n;i++)
        cin>>a[i];
	for(LL i=1;i<=m;i++){
		LL u,v,w;
		cin>>u>>v>>w;
		G[u].push_back({v,w});
		G[v].push_back({u,w});
	}
	priority_queue<pii,vector<pii>,greater<pii>>q;
	for(LL i=0;i<=n;i++)
        dis[i]=LLONG_MAX;
	q.push({a[1],1}),dis[1]=a[1];
	while(q.size()){
		pii pr=q.top();
		LL w=pr.first;
		LL u=pr.second;
		q.pop();
		if(w>dis[u])
            continue;
		for(auto i:G[u]){
			LL v=i.to,nxt_u=w+i.far+a[v];
			if(nxt_u<dis[v]){
				dis[v]=nxt_u;
				q.push({nxt_u,v});
			}
		}
	}
	for(LL i=2;i<=n;i++)
        cout<<dis[i]<<" ";
    return 0;
}

E.Count Arithmetic Subsequences(动态规划)

题意:

给你一个长度为 N N N的序列 A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\dots,A_N) A=(A1,A2,,AN)。对于每个 k = 1 , 2 , … , N k=1,2,\dots,N k=1,2,,N,求长度为 k k k A A A的子序列(不一定连续)中算术序列的个数(答案对 998244353 998244353 998244353取模)。如果两个子序列取自不同的位置,即使它们作为序列是相等的,也是有区别的。

分析:

对于一个等差数列,我们只要知道它的第一项,第二项,就能推出第 n n n项的值:设第一项为 w 1 w_1 w1,第二项为 w 2 w_2 w2,那么第 n n n项的值就是 w 1 + ( n − 1 ) × ( w 2 − w 1 ) w_1+(n−1)×(w_2−w_1) w1+(n1)×(w2w1),即首项加上 ( n − 1 ) (n−1) (n1)倍的公差。

那么这个时候我们就可以考虑动态规划了,可以把前两项是哪个位置直接塞到 d p dp dp的状态里。注意这里是要存位置,如果是值显然是存不下的。一种更简洁的办法是枚举前两项的位置,进行二维 d p dp dp,把贡献累加到答案里即可。

假设这两个位置为 p 1 , p 2 p_{1},p_{2} p1,p2 ( p 1 < p 2 ) (p_1\lt p_2) (p1<p2)。设 f i , j f_{i,j} fi,j表示前 i i i个数,选了 j j j个的方案数。那么易得 f i , j = f i − 1 , j + f i − 1 , j − 1 [ a i = a p 1 + ( a p 2 − a p 1 ) × ( j − 1 ) ] f_{i,j}=f_{i−1,j}+f_{i−1,j−1}[a_i=a_{p1}+(a_{p2}−a p_1)×(j−1)] fi,j=fi1,j+fi1,j1[ai=ap1+(ap2ap1)×(j1)]

注意一下这里的初值是 f p 2 , 2 = 1 f_{p_2,2}=1 fp2,2=1,其它均为 0 0 0。计算时只计算大于 p 2 p_2 p2处的 d p dp dp值。

然后对于每个 i i i,将 k = i k=i k=i的答案加上 f n , i f_{n,i} fn,i即可。

这里默认这个等差数列有至少两个元素,对于 k = 1 k=1 k=1的情况,显然答案为 n n n

代码:

#include<bits/stdc++.h>
typedef long long LL;
using namespace std;

const int MOD=998244353;
int f[85][85],a[85],ans[85];

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);cout.tie(0);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) 
		cin>>a[i];
	for(int p1=1;p1<=n;p1++){
		for(int p2=p1+1;p2<=n;p2++){
			memset(f,0,sizeof(f));
			f[p2][2]=1;
			int d=a[p2]-a[p1];
			for(int i=p2+1;i<=n;i++){
				for(int j=0;j<=i;j++){
					f[i][j]=f[i-1][j];
					if(a[i]==1ll*d*(j-1)+a[p1]) 
						f[i][j]=(f[i][j]+f[i-1][j-1])%MOD;
				}
			}
			for(int i=1;i<=n;i++)
				ans[i]=(ans[i]+f[n][i])%MOD;
		}
	}
	cout<<n<<" ";
	for(int i=2;i<=n;i++)
		cout<<ans[i]<<" ";
	return 0;
}

赛后交流

在比赛结束后,会在交流群中给出比赛题解,同学们可以在赛后查看题解进行补题。

群号: 704572101,赛后大家可以一起交流做题思路,分享做题技巧,欢迎大家的加入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值