2023杭电多校第七场 - B D K M

比赛地址:传送门
赛时过了 4 题哈,继续加油~
1002 B 规律题
1004 D 构造题 有点思维
1011 K 思维签到题
1013 M 思维题 妙 ~


1002 B. Random Nim Game

题意
Alice and Bob 想随机地玩 nim 游戏。Alice 先手。每回合玩家都随机的选择一堆石子,随机地选择移走该堆石子中的任意个石子,谁取走最后一颗石子胜利。问你最后 Alice 赢的概率取模 998244353

思路
问概率,那我们先考虑对于一堆石子的情况,即 \(n = 1\) 时,可以发现一个规律:$P(a_1 = 1) = 1, P(a_1 > 1) = \frac{1}{2} $。

可以通过归纳法获得这个性质:推得前几项都是符合的,当 \(a_1 = x\) 时, $P(a_1 = x) = \frac{1}{x} + \frac{1}{x} \times (x - 2) \times \frac{1}{2} = \frac{1}{2} $,即全拿的概率 + 剩下的个数超过 1 个的概率。

那么当 $n > 1 $ 时,当每堆石子数均为 1 时,每堆石子均只有一次取的机会,那么就看石子堆数是否为偶数即可;当有至少一堆石子数不为 1 时,那么对于先手而言,先手操作每一堆获胜的概率都是 \(\frac{1}{2}\),则先手获胜的概率与选择无关,均为 \(\frac{1}{2}\)

代码

void solve(){
	int n;
	cin >> n;
	bool f = false;
	for(int i = 0; i < n; ++ i){
		int t; cin >> t;
		if(t > 1) f = true;
	}
	if(f) cout << "499122177";
	else cout << (n % 2 ? 1 : 0) << '\n';
	return ;
}

1004 D. Medians Strike Back - 还在理解中

题意
下面定义一个长为 n 的序列的中位数

  • 当 n 为奇数的时候,最中间的数即为中位数
  • 当 n 为偶数的时候,最中间的两个数中的出现次数多的为中位数,出现次数相同时数字小的为中位数

定义该序列的次数为中位数出现的次数,定义该序列中所有连续子序列中的最大次数为该序列的密度

你需要找一个长为 n 的序列 A,满足 \(1\le A_i \le 3\) 且其密度最小,输出这个密度

思路
神仙队友手搓几个例子,推出了下面的式子,,,恕我还无法理解 qwq
如果你问我这个构造思路是怎么出来的,我的建议是再看看题,自己再想想

题解思路
若答案是 B,则构造为 \(\{1,3,1,3,...,1,3,2,2,1,3,...\}\),即 B 个 1 3 之后 2 2 构成一循环
证明:任何一个长度为 2B + 2 的区间一定至少有 2 个 2,上述构造同时满足 2 最少

依据这个构造思路,我们可以明白每个密度 B 所能实现的最长的序列
B = 1 - 3:123
B = 2 - 10:1313 22 1313
B = 3 - 15:131313 22 131313 2
B = 4 - 28:13131313 22 13131313 22 13131313
B = 5 - 35:1313131313 22 1313131313 22 1313131313 2
\(\vdots\)
\(B = i - (\frac{i}{2} + 1) \times i * 2 + B :1313131313... 22 13...\)
\(\frac{i}{2} + 1\) 组 2B-131313... + B 个2

代码
神仙队友赛时代码

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int maxm = 4e4 + 5, inf = 0x3f3f3f3f, mod = 998244353;
int f[maxm + 1];

void pre(){
	for(int i = 0; i <= maxm; ++ i)
		f[i] = (i / 2 + 1) * i * 2 + i;
	return ;
}

void solve(){
	int n;
	cin >> n;
	auto it = lower_bound(f, f + maxm + 1, n) - f;
	cout << it << '\n';
	return ;
}

signed main(){
	pre();
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}

1011 K. Three Operations

题意
给你三个数 x, a, b。你可以执行三种操作:

  • \(x = x - 1\)
  • $x = \lfloor \frac{x + a}{2} \rfloor $
  • $x = \lfloor \sqrt{x + b} \rfloor $

思路
模拟题意即可,判断题目所给的三种方式哪种生成的 x 最小,每次均选择这种最小的方式即可。选择后两种操作的次数一定不超过 \(O(\log x)\) 次,而第一种操作需要 \(O(x)\) 次,所以可以直接暴力枚举三种方式,在只能选择第一种操作后退出循环,再利用操作一即可。

代码

void solve(){
	ll x, a, b, ans = 0;
	cin >> x >> a >> b;
	while(1){
		ll t = x;
		x = min(x - 1, min((x + a) / 2, (ll) sqrt(x + b)));
		++ ans;
		if(x == t - 1) break;
	}
	ans += x;
	cout << ans << '\n';
	return ;
}

1013 M. Minimal and Maximal XOR Sum

题意
给你一个长为 n 的排列,你可以进行任意次操作。每次操作你可以选择一个区间 \([l, r]\),将这个区间的数整体翻转,即翻转 \(p_l,p_{l + 1},...,p_r\)\(p_r,p_{r - 1},...,p_l\),并且定义这次操作的价值为 \(r - l + 1\)
你可以执行任意次操作使得整个排列满足 \(p_i = i\)
问你最终所有操作价值的异或和的最小值和最大值

思路
说说自己的理解哈~
首先摘记一个结论:一个排列中的任意两个元素对调位置,排列改变奇偶性
奇/偶排列:逆序数为奇数/偶数的排列

再有就是如果我们将区间进行翻转,再利用两两交换将该区间恢复原样,那么我们可以获得一个区间长 k 的价值的操作和 $\frac{k(k -1)}{2} $个价值为 2 的操作。如果说我们想利用这个操作使得最后的异或和尽可能大,我们可以翻转任意连续的 \(2^m(m \ge 2, 2^m \le n)\) 个数,再利用这一操作复原区间,则此时可以注意到一件事情,就是我们获得了价值为 \(2^m\) 的操作和偶数个价值为 2 的操作,也就相当于不改变排列获得 \(2^m(m \ge 2)\) 的价值
还可以注意到当 \(m = 0\) 时,也不改变原排列而获得价值为 1 的操作

据此我们可以知道,我们可以任意改变答案二进制位上的除了 \(2^1\) 位的任意一位,为了保持这些已经获得的价值,我们剩下的操作还剩两两交换。

如果说我们想要通过操作将当前排列变成目标排列,且仅利用两两交换时,操作次数呈现一定规律。当前排列为奇排列,那么必定需要奇数次次两两交换,偶排列必定需要偶数次。所以对于偶排列,一定可以通过两两交换的形式实现目标排列,且由于交换次数为偶数,所以价值最小异或和为0,此时剩余二进制位均赋 0 即可;而对于奇排列,价值最小异或和则为2。
最大异或和即在最小异或和的基础上使得所有的二进制位为 1 即可

所以这题的关键在于发现我们可以通过操作复原序列而获得不同价值

代码

//>>>Qiansui
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define mem(x,y) memset(x, y, sizeof(x))
#define debug(x) cout << #x << " = " << x << '\n'
#define debug2(x,y) cout << #x << " = " << x << " " << #y << " = "<< y << '\n'
//#define int long long

using namespace std;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> pull;
typedef pair<double, double> pdd;
/*

*/
const int maxm = 1e5 + 5, inf = 0x3f3f3f3f, mod = 998244353;
int n, p[maxm];
ll sum;

struct BIT{
	int num;
	vector<ll> c;
	BIT(int x = maxm) : num(x), c(x + 1, 0){}
	int lowbit(int x){ return x & (-x); }

	void update(int x, int v){
		while(x <= num){
			c[x] += v; x += lowbit(x);
		}
		return ;
	}

	ll getsum(int x){
		ll res = 0;
		while(x){
			res += c[x]; x -= lowbit(x);
		}
		return res;
	}
};

void solve(){
	sum = 0;
	cin >> n;
	BIT bit(n);
	for(int i = 1; i <= n; ++ i){
		cin >> p[i];
		sum += i - 1 - bit.getsum(p[i]);
		bit.update(p[i], 1);
	}
	ll minn, maxx = 0;
	minn = sum % 2 ? 2 : 0;
	int wei = log2(n);
	for(int i = wei; i >= 0; -- i){
		if(i == 1){
			maxx += sum % 2 ? 2 : 0;
		}else{
			maxx +=  1 << i;
		}
	}
	cout << minn << ' ' << maxx << '\n';
	return ;
}

signed main(){
	ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
	int _ = 1;
	cin >> _;
	while(_ --){
		solve();
	}
	return 0;
}
posted on 2023-08-08 16:57  Qiansui  阅读(41)  评论(0编辑  收藏  举报