浙江农林大学2022年新生杯程序设计竞赛(同步赛)复习

浙江农林大学2022年新生杯程序设计竞赛(同步赛)

1,D,jbgg爆金币咯

思想:dp优化

两个人,设为a,b,想知道x血量a能否获胜,只需要知道被a所以技能砍后的血量状态b能否获胜(不能,那a在x血量可以赢),由此递推,会推到x最后小于0的情况(答案)

我们容易想到dfs遍历,返回1,表示当前人可以在该血量获胜,0则不行,当然,如果x小于0,直接返回0(必败,boss已经死了,没你表现的机会)

为了方便,设a为0,b为1,方便两人在数组用异或转化(因为是回合制,轮流来)

剪枝优化:为防止重复遍历,我们对更新过的dp数组访问时直接返回

#include <bits/stdc++.h>
using namespace std;
#define ll     long long
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int INF = 0x3f3f3f3f;
const int N = 2e3 + 100;

int a[5][10];//存储两人的技能数量及其伤害
int dp[5][N];

bool dfs(int x, bool f)
{
	if (x <= 0)return 0;//必败
	if (dp[f][x] != -1)return dp[f][x];//已经更新过
	for (int i = 1; i <= a[f][0]; ++i)
		{
			if (!dfs(x - a[f][i], f ^ 1) )return (dp[f][x] = 1);//如果我这一到下去,对手返回0,说明他赢不了,那么我就是赢了,更新dp并返回
		}//注意,dfs的f每次都有异或——更换对手--f ^ 1
	return (dp[f][x] = 0);//所以技能都用过,对手还是赢,那么我认输行了吧

}

int main()
{
	int t;
	cin >> t;
	while (t--)
		{
			int x;
			cin >> a[0][0] >> a[1][0] >> x;//0位置存储技能数量
			for (int i = 1; i <= a[0][0]; ++i)cin >> a[0][i];
			for (int i = 1; i <= a[1][0]; ++i)cin >> a[1][i];
			memset(dp, -1, sizeof(dp));//重置dp,为-1表示没有访问
			cout << (dfs(x, 0) ? "AsindE" : "slwang") << endl;//先手是a,所以返回1,a赢

		}

	return 0;
}

2,I,异或和

思想:二进制转化

求解异或和很简单,重点是怎么任意组合

观察到:异或是每一位单独进行,那么我们和是不是也可以一位一位处理呢——这样我们就可以对每一位使用高中的组合公式

我们统计数组中每一位1的个数,我们知道集合n个元素有2^n的组合,所以对于每一位,0与1的组合有(2^x(1的个数-1)*2*x(0的个数))1的个数减1是因为我们1的组合总会出现一半组合是偶数个1(包括0)的情况,即该异或和为0,无意义,所以减,减在1上面,使一个1也没有时值为0.

对于每一位的值,还要*1<<i,表示其二进制位置的值

#include <bits/stdc++.h>
using namespace std;
#define ll     long long
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int INF = 0x3f3f3f3f;
const int N = 1e5 + 100;

int mod = 998244353;
ll pows[N];//pows开的空间是N,因为不是简单的代表2的几次方,而是1的个数
ll cnt[30];
int main()
{
	ll n, x;
	cin >> n;
	pows[0] = 1;
	for (int i = 1; i <= n; ++i)pows[i] = pows[i - 1] * 2 % mod;//后面值很大,一定要取模
	for (int i = 1; i <= n; ++i)
		{
			cin >> x;
			for (int j = 22; j >= 0; --j)if ((x >> j) & 1)cnt[j]++;//记录每一位二进制
		}
	ll ans = 0;
	for (int i = 0; i <= 22; ++i)ans = (ans + ((pows[cnt[i] - 1] * pows[n - cnt[i]]) % mod) * (1<<i)%mod) % mod;
	cout << ans << endl;
	return 0;
}

3,L,jbgg想要n

思路:加的时候发现每次只需要保留余数部分就会,暴力遍历加标记剪枝

#include <bits/stdc++.h>
using namespace std;
#define ll     long long
typedef unsigned long long ull;
typedef pair<long long, long long> pll;
const int INF = 0x3f3f3f3f;
const int N = 1e6 + 100;

int main()
{
	ll n, m, p;
	bitset<N>vis;
	cin >> n >> m >> p;
	ll k = 1;
	while (n / k)k *= 10;
	n%=p;//n或者k都可能比p大,直接加可能会错,先取余,不然n,k极大时乘起来直接爆
	k%=p;
	ll t = 0, ans =n;
	for (int i = 1; i <= m; ++i)
		{
			t = (t * k%p + n) % p;
			if (vis[t])break;
			vis[t] = 1;
			ans = max(ans, t);
		}
	cout << ans;
	return 0;
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值