AtCoder Beginner Contest 245 C~F题解

AtCoder Beginner Contest 245

C

题意:给出序列 A , B A,B A,B, 找出满足下列条件的序列 X X X是否存在:

  1. X i = A i 或 X i = B i X_i=A_i或X_i=B_i Xi=AiXi=Bi , X X X的长度为 N N N.
  2. ∣ X i − X i + 1 ∣ ≤ K |X_i-X_{i+1}|\leq K XiXi+1K ,对于 1 ≤ i ≤ N − 1 1\leq i \leq N-1 1iN1.

考虑dp, 定义 d p [ i ] [ 0 ] 为选了 i 个数,且第 i 个数为 A 中元素, d p [ i ] [ 1 ] 为第 i 个数为 B 中元素 定义dp[i][0]为选了i个数,且第i个数为A中元素,dp[i][1]为第i个数为B中元素 定义dp[i][0]为选了i个数,且第i个数为A中元素,dp[i][1]为第i个数为B中元素

d p 数组的值表示是否可以达成 dp数组的值表示是否可以达成 dp数组的值表示是否可以达成

然后根据题目的条件来进行转移即可。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=2e5+10,mod=998244353;
int a[N],b[N];
int dp[N][2];
void work()
{
	int n,k;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	dp[1][0]=dp[1][1]=1;
	for(int i=2;i<=n;i++){
		if(abs(a[i]-b[i-1])<=k) dp[i][0]|=dp[i-1][1];
		if(abs(a[i]-a[i-1])<=k) dp[i][0]|=dp[i-1][0];
		if(abs(b[i]-a[i-1])<=k) dp[i][1]|=dp[i-1][0];
		if(abs(b[i]-b[i-1])<=k) dp[i][1]|=dp[i-1][1];
	}
	if(dp[n][0]||dp[n][1]) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	
}
signed main()
{
	int t;
	t=1;
	while(t--) work();

	return 0;
}

D

题意:给出两个多项式, A , C A,C A,C, C 是由 A ∗ B 得到的 C是由A*B得到的 C是由AB得到的,现在让你求出B的每一项的系数。

1 ≤ N ≤ 100 1\leq N \leq 100 1N100.

根据题意,我们可以直接来模拟两个多项式相乘即可,由于数据范围很小,因此可以暴力的做,需要注意的是要从最后一项往前面推,因为题目保证了 A N ! = 0 A_N!=0 AN!=0, 一开始我从前往后推出现了RE,因为除以0所以会出现错误。

下标的关系可以纸上模拟一下就出来了。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=2e3+10,mod=998244353;
int a[N],c[N];
int b[N];
void work()
{
	int n,m;
	cin>>n>>m;
	for(int i=0;i<=n;i++) cin>>a[i];
	for(int i=0;i<=n+m;i++) cin>>c[i];
	b[m]=c[n+m]/a[n];
	
	for(int i=m-1;i>=0;i--){
		int sum=0;
		int cnt=0;
		for(int j=n-1;j>=n-(m-1-i)-1;j--){
			cnt++;
			sum+=b[i+cnt]*a[j];
		}
		b[i]=(c[i+n]-sum)/a[n];
	}
	for(int i=0;i<=m;i++) cout<<b[i]<<" ";
	cout<<endl;

}
signed main()
{
	int t;
	t=1;
	while(t--) work();

	return 0;
}

E

题意:有 N N N个物品, M M M个箱子,物品和箱子都有长度和宽度。问能否让这些物品成功的放入箱子里,一个箱子最多放一个物品,且物体不能旋转。

首先想到的是贪心的放,对于某个物品来说,我们要尽量放到小的箱子里,考虑长和宽两个因素比较困难,因此我们可以对其按长排序,只考虑长,若不存在某个宽度满足条件,则说明无法放入。

那要如何来贪心呢?考虑先把小的箱子放进去,这样会有什么问题呢?若后面有很多箱子可以放,就会导致决策出现问题:例如若当前两个物品长和宽为 2 , 3 和 3 , 4 2,3 和 3,4 2,33,4 , 箱子为 3 , 4 和 5 , 3 和 6 , 3 3,4和5,3和6,3 3,45,36,3, 若按我们贪心,要把2,3放进3,4,然后会发现3,4放不进去了,因此这样无法保证结果的正确性。 那么从后往前考虑贪心,先把大的放进去,为什么这样是对的?因为我们先放大的,所以放大的决策不会影响放小的选择,大的能够放进去,则就判断下一个能否放入,否则就不能放入。

在具体的实现中,可以使用 m u l t i s e t multiset multiset来维护箱子的宽度,然后使用双指针来模拟遍历物品和箱子。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=4e5+10,mod=998244353;

pii a[N],b[N];
multiset<int> s;
void work()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) {
		int x; cin>>x;
		a[i].x=x;
	}
	for(int i=1;i<=n;i++) {
		int x; cin>>x;
		a[i].y=x;
	}
	for(int i=1;i<=m;i++) {
		int x; cin>>x;
		b[i].x=x;
	}
	for(int i=1;i<=m;i++) {
		int x; cin>>x;
		b[i].y=x;
	}
	bool f=1;
	sort(a+1,a+1+n); sort(b+1,b+1+m);
	for(int i=n,j=m;i>=1;i--){
		while(j>=1&&a[i].x<=b[j].x){
			
			s.insert(b[j].y);
			j--;
		}
		auto id=s.lower_bound(a[i].y);
		if(id==s.end()) {
			f=0; break;
		}
		else s.erase(id);
	}
	if(f) cout<<"Yes"<<endl;
	else cout<<"No"<<endl;
	
}
signed main()
{
	ios;
	int t;
	t=1;
	while(t--) work();

	return 0;
}

F

题意:给定一个有向图,问有从多少个点出发可以无限的走下去。

首先容易想到,所有能够走到环上的点和组成环的点的数量和即是答案。

那么如何去统计这些点的数量呢? 并不是很好统计,(好像用tanjan缩点也可以统计),正难则反,我们考虑有多少点无法一直走下去。

其实此时就可以想到反向建边了,在反向建边的情况下,考虑有多少个点可以走到环,则这些点实际上就是无法走到环的点。

用拓扑排序就可以很好的解决这个问题,有多少点能够入队,那么这些点就是实际上无法走到环的点。

code:

#include<bits/stdc++.h>

using namespace std;
#define endl '\n'
#define ios std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define int long long
#define x first
#define y second
typedef pair<int,int> pii;

//head
const int N=2e5+10,mod=998244353;

int e[N],ne[N],h[N],idx;
int in[N];
int n,m,ans;
void add(int a,int b)
{
	e[idx]=b; ne[idx]=h[a]; h[a]=idx++;
}

void tp()
{
	queue<int> q;
	for(int i=1;i<=n;i++){
		if(in[i]==0) q.push(i);
	}
	while(q.size()){
		int t=q.front(); q.pop();
		ans--;
		for(int i=h[t];~i;i=ne[i]){
			int j=e[i];
			if(--in[j]==0) q.push(j);
		}
	}
}
void work()
{
	/*
	拓扑排序判环:若所有点都能入队,则其不存在环,若无法全部入队,则一定存在环(环之后的点无法遍历到)
	本题我们要找的是有多少点能够走到环的地方,不如反向思考,从环能够走到哪些点,这时就可以反向建边
	进行拓扑排序,这样我们无法遍历到的点实际上就是答案(有多少点能走到环上)
	可以遍历的点就是那些无法走到环上的点
	*/
	memset(h,-1,sizeof h);
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int a,b;
		cin>>a>>b;
		add(b,a); //反向建边
		in[a]++;
	}
	ans=n;
	tp();
	cout<<ans<<endl;

}
signed main()
{
	int t;
	t=1;
	while(t--) work();

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值