A - Divide and Conquer
题意:给出序列a,设b为a中元素总和。你可以选择a中任意元素,将它除以二(向下取整)。问最少需要多少次可以使b为偶数
思路:将a划分为奇偶两个集合。a中偶数元素的数量是奇是偶对题目没有影响,要使b为偶数,需要知道奇数元素的个数
若奇数元素是偶数,则b一开始就是偶数,输出0
否则
1.对于奇数集合中每个元素进行操作,找出可以使一个元素变成偶数需要的最少操作x
2.对于偶数集合中每个元素进行操作,找出可以使一个元素变成奇数需要的最少操作y
ans = min(x,y)
const int N = 50 + 10, M = 5010, INF = 2e9, MOD = 1e9 + 7;
int a[N];
//注意观察N的大小
void solve() {
int n;
cin >> n;
int cnt0 = 0, cnt1 = 0;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
if (a[i] % 2 == 1) cnt1 ++;
else cnt0 ++;
}
if (cnt1 % 2 == 0) {
cout << 0 << endl;
}else {
int ans = 1e9;
for (int i = 1; i <= n; i ++) {
if (a[i] % 2 == 1) {
int t = 0;
while (a[i] % 2 != 0) {
t ++;
a[i] /= 2;
}
ans = min(ans, t);
}else {
int t = 0;
while (a[i] % 2 != 1) {
t ++;
a[i] /= 2;
}
ans = min(ans, t);
}
}
cout << ans << endl;
}
}
B - Make Array Good
题意:给出序列a,你可以进行操作,每次操作可以使a中一个元素加上小于等于它的一个数x,(x>0)。要求操作数不超过n,且要输出最终操作次数和每次操作的下标和x
思路:找到序列中最小的元素mina,然后后面元素都以它为最小因子进行增添即可。若a中存在mina的倍数或者存在某元素操作后为mina的倍数,则将mina更新为较大的那个。
const int N = 1e5 + 10, M = 5010, INF = 2e9, MOD = 1e9 + 7;
PII a[N];
//注意观察N的大小
void solve() {
int n;
cin >> n;
for (int i = 1; i <= n; i ++) {
cin >> a[i].first;
a[i].second = i;
}
sort(a + 1, a + 1 + n);
int tmp = a[1].first;
vector<PII> ans;
for (int i = 2; i <= n; i ++) {
if (a[i].first % tmp == 0) {
if (a[i].first > tmp) tmp = a[i].first;
}else {
int t = a[i].first / tmp;
int x = tmp * (t + 1) - a[i].first;
tmp = tmp * (t + 1);
ans.push_back({a[i].second, x});
}
}
cout << ans.size() << endl;
for (auto [x, y] : ans) cout << x << ' ' << y << endl;
}
C. Binary Strings are Fun
题意:给出一个二进制字符串,你可以将它“扩充”,即在每个元素中间增加一个二进制数(0/1),若ai在(1,i)上是中位数,那么说这个前缀是好的。问从(1,n)中将其扩充后可以有多少好的前缀
思路:找规律。如01,10这类,中间若要扩充只可能有一种情况,而11,00这类可以有两种情况。前面已经确定的序列对后面也有影响。
void solve() {
int n;
cin >> n;
string s;
cin >> s;
LL sum = 1, tmp = 1; //方案总数sum,过程方案数tmp
for (int i = 1; i < s.size(); i ++) {
int x = s[i - 1] - '0', y = s[i] - '0';
if (x != y) tmp = 1; //如果相邻两个数不相同,那么从开始到当前位置仅有一种可能的方案
else tmp *= 2; //若相同,那么方案数乘以当前位置扩充的可能
sum += tmp;
sum %= MOD;
tmp %= MOD;
}
cout << sum << endl;
}
D - GCD Queries
题意:交互题,给出一个n,代表有一个含有n个数字的排列。这个排列是隐藏的,需要通过交互找到数字0的位置。每次提问给出两个数的下标,会回答这两个数的gcd,若这两个数:x,y中有一个为0则可以输出
思路:直接去找到0很难,那可以用排除法,将不是0的数字排除,剩下的就只有0
设有下标l, mid, r。每次进行两次询问x = gcd(l, mid), y = gcd(mid, r)。
若x == y,则mid所代表的元素绝不可能为0
若x > y,则x所代表的元素不可能为0,
若y > x,则y所代表的元素不可能为0.
ps:如果关了同步流需要注释掉这部分,否则会出现
void ask(int x, int y) {
cout << "? " << x << " " << y << endl;
}
//注意观察N的大小
void solve() {
int n;
cin >> n;
queue<int> q;
for (int i = 1; i <= n; i ++) q.push(i);
while (q.size() > 2) {
int l = q.front();
q.pop();
int r = q.front();
q.pop();
int mid = q.front();
q.pop();
ask(l, mid);
int x, y;
cin >> x;
ask(mid, r);
cin >> y;
if (x == y) {
q.push(l);
q.push(r);
}else if (x > y) {
q.push(l);
q.push(mid);
}else {
q.push(r);
q.push(mid);
}
}
int x, y;
x = q.front(), q.pop();
y = q.front(), q.pop();
cout << "! " << x << " " << y << endl;
cin >> n;
}