AtCoder Beginner Contest 247 C~F题解

AtCoder Beginner Contest 247

C

题意:序列S=1, S n = S n − 1 , n , S n − 1 S_n=S_{n-1},n,S_{n-1} Sn=Sn1,n,Sn1, 即 S 1 = 1 , S 2 = 1 , 2 , 1 S_1=1, S_2=1,2,1 S1=1,S2=1,2,1, 给出n,求 S n S_n Sn为多少·。

1 ≤ N ≤ 16 1 \leq N \leq 16 1N16.

由于N比较小,因此我们可以暴力的dfs来输出,主要是对递归的理解,当然还有其他的方法。

code:

#include<bits/stdc++.h>

using namespace std;

vector<int> v;
void dfs(int n)
{
	if(n==1){
		v.push_back(1);
		return ;
	}
	dfs(n-1);
	v.push_back(n);
	dfs(n-1);
}
signed main()
{
	int n;
	cin>>n;
	dfs(n);
	for(auto vi:v){
		cout<<vi<<" ";
	}
	return 0;
} 

D

题意:给定一个队列,初始为空,执行下面两种操作:

  1. 往队尾插入c个价值为x的球。
  2. 取出队头前c个球,并输出这c个球的价值。

1 ≤ c ≤ 1 0 9 1 \leq c \leq 10^9 1c109

考虑使用双端队列,由于c比较大,暴力插入肯定不行,因此用pair来记录每个球的数量。然后就是根据题意来模拟啦。

code:

#include<bits/stdc++.h>

using namespace std;
#define int long long
typedef pair<int,int> pii;
vector<int> v;

signed main()
{
	deque<pii> dq;
	int q,tt;
	cin>>q;
	int x,c;
	
	while(q--){
		cin>>tt;
		if(tt==1){
			cin>>x>>c;
			dq.push_back({c,x});
		}
		else 
		{
			int ans=0;
			cin>>c;
			while(c>0)
			{
				auto t=dq.front();
				dq.pop_front();
				if(t.first<=c){
					ans+=t.second*t.first;
					c-=t.first;
				}
				else {
					ans+=t.second*(c);
					dq.push_front({t.first-c,t.second});
					c=0;
				}	
			}
			cout<<ans<<endl;
		}
	}
	
	return 0;
} 

E

题意:给定序列A,问共有多少对(L,R),满足:

  1. 1 ≤ L , R ≤ N 1 \leq L,R \leq N 1L,RN.
  2. A L , A L + 1 . . . A R A_L,A_{L+1}...A_R AL,AL+1...AR 的最大值为X, 最小值为Y.

做的时候没想那么多,直接对着样例开始模拟了起来,做完发现这题竟然那么多解法。不过我的解法也很不错

大致思路就是类似于双指针,固定区间的右端点,然后找满足条件的最远的左端点。例如当前右端点为R,合法的最远左端点为L,则这个点对答案的贡献为R-L,。

具体怎么实现呢?我们可以从前往后扫描,分别记录下 X, Y,以及比X大或比Y小的数字的位置,然后计算贡献即可。这样说也不是很好理解,可以根据代码对最后一个样例模拟,会好理解一些。

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;
int a[N];

void work()
{
	int n,x,y;
	cin>>n>>x>>y;
	int px=0,py=0,p=0;
	int ans=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		if(a[i]<y||a[i]>x) p=i;
		if(a[i]==x) {
			px=i;
		}
		if(a[i]==y){
			py=i;
		}
		//cout<<px<<"###"<<py<<endl;
		if(px&&py){
			int mn=min(px,py);
			ans+=max(0LL,mn-p);
		}
	}
	cout<<ans<<endl;
	
}

signed main()
{
	//ios;
	int t;
	t=1;
	//cin>>t;
	while(t--) work();

	return 0;
}

F

题意:有N张卡片,正反面都印着数字,正面印的数字和反面印的数字都是N的一个排列,问有多少种方案,使得取出的球正面和反面的数字构成N的一个排列。

这个题还是需要来发掘一些性质的,我们把它抽象出来,即可看成正反面代表连一条边。 这是个很关键的性质,说明整个图是由一个个独立的环构成的,根据乘法原理,总的方案数等于各个环的方案数的乘积。

那一个点数为N的环的方案数怎么求呢?我们可以采用递推式来求,

设 d p [ i ] 为当前有 i 个点,且构成一个环的方案数,那么对于 d p [ i ] , 由谁转移过来呢?因为每个点连接着两个点,因此可以由 d p [ i − 2 ] 和 d p [ i − 1 ] 转移过来,可以画图方便理解 设dp[i]为当前有i个点,且构成一个环的方案数,那么对于dp[i],由谁转移过来呢?因为每个点连接着两个点,因此可以由dp[i-2]和dp[i-1]转移过来,可以画图方便理解 dp[i]为当前有i个点,且构成一个环的方案数,那么对于dp[i],由谁转移过来呢?因为每个点连接着两个点,因此可以由dp[i2]dp[i1]转移过来,可以画图方便理解

然后我们使用并查集来判断每个环的大小即可。感觉这题难的还是在于怎么求每个环的方案数

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 f[N],p[N];
int cnt[N];
int find(int x)
{
	if(x!=p[x]) p[x]=find(p[x]);
	return p[x];
}
void work()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1;i<=n;i++) cin>>b[i];
	f[1]=1; f[2]=3;
	for(int i=3;i<=n;i++){
		f[i]=(f[i-1]+f[i-2])%mod;
	}
	for(int i=1;i<=n;i++) p[i]=i;
	for(int i=1;i<=n;i++){
		int pa=find(a[i]),pb=find(b[i]);
		if(pa!=pb) p[pb]=pa;
	}
	for(int i=1;i<=n;i++){
		cnt[find(i)]++;
	}
	int ans=1;
	for(int i=1;i<=n;i++){
		if(cnt[i]){
			ans=ans*f[cnt[i]]%mod;
		}
	}
	cout<<ans<<endl;
	
}

signed main()
{
	//ios;
	int t;
	t=1;
	//cin>>t;
	while(t--) work();

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值