康复训练.jpg
A.Exciting Bets
题意
给出两个数 a , b a,b a,b,每次操作可以:
- a++;
- a–,b–.
问在任意次操作后最大可能的 g c d ( a , b ) gcd(a,b) gcd(a,b)是多少,且问得到这个结果至少需要操作几次.
分析
根据定理 g c d ( a , b ) = g c d ( a , a − b ) gcd(a,b)=gcd(a,a-b) gcd(a,b)=gcd(a,a−b),由于 a − b a-b a−b不变,找到离 a a a最近的 a − b a-b a−b的倍数即可.
代码
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;
signed main()
{
IOS;
int t;
cin >> t;
while(t--)
{
int x, y;
cin >> x >> y;
if(x == y){
cout << "0 0" << endl;
continue;
}
int d = abs(x - y);
cout << d << ' ';
if(d == 1){
cout << 0 << endl;
continue;
}
x = min(x, y);
cout << min(abs((x % d) - d), x % d) << endl;
}
return 0;
}
B. Customising the Track
题意
给一个数组,你可以自由地以整数为单位分配其中的元素(即保证整个数组的数都是非负数,且数组总和不变即可),要求 ∑ i = 1 n ∑ j = i + 1 n ∣ a i − a j ∣ \sum_{i=1}^n \sum_{j=i+1}^n|a_i-a_j| ∑i=1n∑j=i+1n∣ai−aj∣最小.
分析
假设数组的总和是 s u m sum sum.
贪心,确保 ∣ a j − a i ∣ |a_j-a_i| ∣aj−ai∣尽量小,只要让整个数组所有数都尽量接近平均数即可。对平均数向下取整得到 a v r avr avr,则假设有 x x x个数能修改成 a v r avr avr,那么剩下的 n − x n-x n−x个数就是 a v r + 1 avr+1 avr+1,这个时候的所求结果就是 x ⋅ ( n − x ) x · (n-x) x⋅(n−x),一定为最小。
很容易知道 x = s u m − a v r ⋅ n x=sum-avr·n x=sum−avr⋅n.
代码
/**
* @file :debug.cpp
* @brief :Round 730
* @date :2021-07-07
* @Motto :Love Sakurai Yamauchi Forever
*/
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;
const int maxn = 2e5 + 10;
int a[maxn];
signed main()
{
IOS;
int t;
cin >> t;
while(t--)
{
int n;
cin >> n; int sum = 0;
fors(i, 1, n) cin >> a[i], sum += a[i];
int avr = sum / n;
cout << (sum - avr * n) * (n - (sum - avr * n)) << endl;
}
return 0;
}
C. Need for Pink Slips
本题读懂题即可AC.
题意
现有三个物品 C , M , P C,M,P C,M,P,随机从他们中取一个,被取到的概率分别为 c , m , p c,m,p c,m,p. 你的目标是取到 P P P. 另外,还有一个小数 v v v, v v v是这样起作用的:
- 如果取到了 P P P,直接结束.
- 如果取到了 A ≠ P A \neq P A=P,且 0 < a ≤ v 0 < a \leq v 0<a≤v,那么 a a a变为0,且状态变为不可用,剩下的可用物品将概率 a a a平分(例如剩下2个都可用,那么每个都加上 a 2 a \over 2 2a;如果只有一个可用,就让它加上 a a a)
- 如果 a > v a>v a>v,那么 a a a变为 a − v a-v a−v,剩下的可用物品将概率 v v v平分.
概率为0的物品当然抽不到啦
若抽 x x x次抽到了 P P P,问 x x x的期望.
分析
裸dfs,讨论一下各个状态。假设每层递归当前的概率为 N N N,每层递归都从 c , m , p c,m,p c,m,p中选一个,选到 c c c或者 m m m时,按题目要求修改 c , m , p c,m,p c,m,p,然后递归下一层,下一层的概率为 N ⋅ a N·a N⋅a; 选到 p p p时,答案加上 N ⋅ p ⋅ 当 前 递 归 抽 的 次 数 N·p·当前递归抽的次数 N⋅p⋅当前递归抽的次数. 最后输出答案,注意一下精度即可。当 N < 精 度 N<精度 N<精度时,就不再向下递归了。
注意
由于 d o u b l e double double数据容易失真,建议不要使用 = = 、 > 、 < ==、>、< ==、>、<,自己写一个基于精度的比较函数.(否则过不了第三个样例)
代码
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;
double ans = 0;
double v;
int cmp(double x, double y)
{
if(fabs(x - y) < 1e-12) return 0;
if(x > y) return 1;
return -1;
}
void dfs(double c, double m, double p, double last, int day)
{
if(last * p < 1e-11 && day != 1) return;
if(cmp(c, v) == 1){
if(cmp(m, 0.0) == 1) dfs(c - v, m + v / 2, p + v / 2, last * c, day + 1);
else dfs(c - v, m, p + v, last * c, day + 1);
}
else if(cmp(c, 0.0) == 0);
else{
if(cmp(m, 0.0) == 1) dfs(0, m + c / 2, p + c / 2, last * c, day + 1);
else dfs(0, 0, p + c, last * c, day + 1);
}
if(cmp(m, v) == 1){
if(cmp(c, 0.0) == 1) dfs(c + v / 2, m - v, p + v / 2, last * m, day + 1);
else dfs(0, m - v, p + v, last * m, day + 1);
}
else if(cmp(m, 0.0) == 0);
else{
if(cmp(c, 0.0) == 1)dfs(c + m / 2, 0, p + m / 2, last * m, day + 1);
else dfs(0, 0, p + m, last * m, day + 1);
}
ans += last * day * p;
}
int n, k;
signed main()
{
// IOS;
int t;
cin >> t;
while(t--)
{
ans = 0;
double c, m, p;
cin >> c >> m >> p >> v;
dfs(c, m, p, 1, 1);
printf("%.6f\n", ans);
}
return 0;
}
D1. RPD and Rap Sheet (Easy Version)
题意
有一个密码 p p p,范围在 [ 0 , n ) [0,n) [0,n),每次可以询问一个数 x x x,然后会返回结果告诉你对了没有(0错,1对).
如果错了,那么密码会被修改为 x ⊕ p x⊕p x⊕p(二进制异或). 你最多可以询问 n n n次.
分析
给一个序列
a
[
i
]
=
i
⊕
(
i
−
1
)
a[i]=i⊕(i-1)
a[i]=i⊕(i−1),然后让
p
p
p与序列元素连续作和,得到:
p
⊕
0
⊕
1
⊕
1
⊕
2
⊕
2
⊕
.
.
.
⊕
(
i
−
1
)
⊕
i
.
p⊕0⊕1⊕1⊕2⊕2⊕...⊕(i-1)⊕i.
p⊕0⊕1⊕1⊕2⊕2⊕...⊕(i−1)⊕i.
由于异或和满足结合律,故将其中相同的两两结合,最后得到
p
⊕
i
p⊕i
p⊕i.
由于 p ∈ [ 0 , n ) p\in [0,n) p∈[0,n),必定存在一个 i = p − 1 i=p-1 i=p−1,于是在与第 p − 1 p-1 p−1项作异或后, p p p变为 p ⊕ ( p − 1 ) p⊕(p-1) p⊕(p−1);而 a [ p ] = p ⊕ p − 1 a[p]=p⊕p-1 a[p]=p⊕p−1,因此下一次必定猜到这个数. 故最多猜 p + 1 p+1 p+1次,也即最多猜 n n n次.
代码
#include <bits/stdc++.h>
#define fors(i, a, b) for(int i = (a); i <= (b); ++i)
#define lson k<<1
#define rson k<<1|1
#define pb push_back
#define lowbit(x) ((x)&(-(x)))
#define mem(a) memset(a, 0, sizeof(a))
#define IOS ios::sync_with_stdio(false), cin.tie(0)
#define int long long
const int inf = 0x3f3f3f3f;
const double dinf = 1e100;
typedef long long ll;
//const ll linf = 9223372036854775807LL;
// const ll linf = 1e18;
using namespace std;
signed main()
{
IOS;
int t;
cin >> t;
while(t--)
{
int n, k;
cin >> n >> k;
cout << 0 << endl;
int x;
cin >> x;
if(x == 1) continue;
fors(i, 1, n - 1)
{
cout << (i ^ (i - 1)) << endl;
cin >> x;
if(x == 1) break;
}
}
return 0;
}