2024牛客寒假算法集训营4 (已更新:B-E)

2024牛客寒假算法集训营4

B

看似是博弈论,实际上是推结论

代码实现

​ 每堆石子不可操作时数量都为1,举例可以推出,每堆石子的可操作数都为a[i]-1,这样一来所有石子的可操作数就能算出来,为奇数时是一个,否则是另一个

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
	int n,x,sum=0;cin>>n;
	for(int i=0;i<n;i++){
		cin>>x;
		sum+=x-1;
	}
	if(sum&1) cout<<"gui";
	else cout<<"sweet";
	return 0;
}




C

对我来说有点难想的模拟题-^-

代码实现

​ 我自己赛时写的是不改变原数组,模拟目标点(x,y)的移动,倒着读入指令,注意只处理与(x,y)点在同一行或同一列的指令

指令的行向右移,则y--

3 3 2 2
abc
def
ghi
1 2
1 1
2 2
(x,y)的变化:
(2,2)->(1,2)—>(1,1)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N=2e2+5;
char a[N][N];
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
	int n,m,p,q,x,y;cin>>n>>m>>x>>y;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}
	}
	cin>>p>>q;
	vector<pair<int,int>>v;
	int op,l;
	for(int i=0;i<q;i++){
		cin>>op>>l;
		v.push_back({op,l});
	}
	while(p--){
		for(int i=q-1;i>=0;i--){
			int e=v[i].first,r=v[i].second;
			if(!(e==1&&r==x||e==2&&r==y)){
				continue;	
			}	
			if(e==2&&r==y){
				x--;
				if(x==0) x=n;//移动到边界要更新
			}
			else if(e==1&&r==x){
				y--;
				if(y==0) y=m;
			}
		}		
	}
	cout<<a[x][y];
	return 0;
}

贴一个模拟的做法~

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N=2e2+5;
char a[N][N];
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
	int n,m,p,q,x,y;cin>>n>>m>>x>>y;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			cin>>a[i][j];
		}
	}
	cin>>p>>q;
	vector<pair<int,int>>v;
	int op,l;
	for(int i=0;i<q;i++){
		cin>>op>>l;
		v.push_back({op,l});
	}
	while(p--){
		for(auto it:v){
			int op=it.first,r=it.second;
			if(op==1){
                //这里就是当时没想出来的做法(⊙﹏⊙)
				int tp=a[r][m];
				for(int i=m;i>=2;i--){
					a[r][i]=a[r][i-1];
				}
				a[r][1]=tp;
			}
			else{
				int tp=a[n][r];
				for(int i=n;i>=2;i--){
					a[i][r]=a[i-1][r];
				}
				a[1][r]=tp;
			}
		}		
	}
	cout<<a[x][y];
	return 0;
}

D

可以说要只要是要用到数学公式或者涉及基础数论的题,我赛时就做不了一点⊙﹏⊙)——这道题就是(⊙﹏⊙)

​ 数组的最大公因数g,b[i]=a[i]/g,满足:a[1]+……a[n]=g*(b[1]+……b[n])=sum,g的变化来自b[i],同时sum(b[i])>=n(也就是每个b[i]为1的情况)

如sum(2 2 4 8)->2*sum(1 1 2 4)->4乘sum(1 1 1 1)

代码实现

​ 所以我们可以枚举sum的因子i,若满足sum/i>=n,则ans++

补充:如何枚举与遍历一个数的因子:

	int n;cin>>n;
	set<int>s;
	for(int i=1;i<=n/i;i++){
		if(n%i==0){
			s.insert(i);
			if(i*i!=n){
				s.insert(n/i);
			}
		}
	}
	cout<<s.size()<<endl;
	for(auto i:s) cout<<i<<" ";
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N=2e5+5;
int a[N];
int gcd(int a,int b){
	if(b==0) return a;
	else return gcd(b,a%b);
}
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
	int n;cin>>n;
	int sum=0,res=1;
	rep(i,1,n){
		cin>>a[i];
		sum+=a[i];
		if(i==1){
			res=a[i];
		}
		else res=gcd(a[i-1],res);
	}
	int ans=0;
	for(int i=1;i<=sum/i;i++){
		if(sum%i==0&&sum/i>=n){
			ans++;
			if(i*i!=sum&&sum/(sum/i)>=n) ans++;
		}
	}
	cout<<ans<<endl;
	return 0;
}

E

其实就是贪心的技巧……但赛时就没想明白

  • 要使答案最大,应该能分组就尽量分组,因为如果一个数就算加到当前组里,它也不可能加到后面的组里,因此我们可以用前缀和实现这一步
  • 如何判断当前组的和是k的倍数?只要这个组的左端点-1与右端点前缀和模k相等即可

       1 1 4 5 1 4
前缀和0 1 2 6 11 12 16
	 ^     ^    ^
得到分组(1,1,4),(5,1)

代码实现

​ 可以用set存前缀和模k的值(即可能的左端点-1),先在set 里加入0,遍历前缀和数组,存下这个值,在这之前还要判断这个值是否在set里出现过,如果出现过,说明找到了一个分组的右端点,就要清空set,同时插入这个右端点当作可能的左端点-1

#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define int long long
#define db(x) cout<<x<<" "<<endl;
#define _db(a,n) for(int i=1;i<=n;i++) cout<<a[i]<<" ";cout<<endl;
#define mem(a) memset(a,0, sizeof(a))
#define rep(i,l,r) for(int i=l;i<=r;i++)
#define per(i,r,l) for(int i=r;i>=l;i--)
const int N=2e5+5;
int a[N];
signed main()
{
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
	int n,k,x;cin>>n>>k;
	rep(i,1,n){
		cin>>x;
		a[i]=a[i-1]+x;
	}
	set<int>s;
	s.insert(0);
	int ans=0;
	rep(i,1,n){
		if(s.count(a[i]%k)){
			ans++;
			s.clear();		
		}
		s.insert(a[i]%k);
	}
	cout<<ans;
	return 0;
}

H

posted @ 2024-02-20 23:53  mono_4  阅读(13)  评论(0编辑  收藏  举报