写在前边
链接:Codeforces Global Round 13
(A,B,C,D)
A. K-th Largest Value
链接:A题链接
题目大意:
有一个字串只由(0、1)组成,有两个操作,(1)是让其中(a_x)变为(1-a_x),(2)是询问数组中第(k)大的数。
思路:
很简单了,字串中元素只在(0、1)中变化,要问第(k)大的数字,那么非(1)即(0),就用一个(map)维护(0,1)的数量就好了
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
//#pragma GCC optimize(2)
//#pragma GCC optimize(3,"Ofast","inline")
using namespace std;
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '
'
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
const int Mod = 10000007;
const int N = 1E5 + 10;
int a[N];
void solve() {
int n, q;
scanf("%d%d", &n, &q);
map<int, int> cnt;
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
cnt[a[i]]++;
}
while (q--) {
int t, x;
scanf("%d%d", &t, &x);
if (t == 1) {
if (a[x] == 1) {
a[x] = 0;
cnt[0]++, cnt[1]--;
} else {
a[x] = 1;
cnt[1]++, cnt[0]--;
}
} else if (t == 2) {
if (cnt[1] >= x) puts("1");
else puts("0");
}
}
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
solve();
return 0;
}
B. Minimal Cost
链接:B题链接
题目大意:
每行一个箱子,第(i)行箱子的坐标为(a_i),你的任务就是推箱子,然后把利用最小的花费把开一条路使得你可以从左上角走到右下角。
思路:
注意
- 每行就一个!
- 你可以随便方向的走!
那么这样就很简单了,只需要在一列箱子中开一个口就好了,然后不断更新答案即可。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
//#pragma GCC optimize(2)
//#pragma GCC optimize(3,"Ofast","inline")
using namespace std;
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '
'
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
const int Mod = 10000007;
const int N = 110;
int a[N];
void solve() {
int n, u, v;
scanf("%d%d%d", &n, &u, &v);
for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
int res = 2e9 + 10;
for (int i = 2; i <= n; i++) {
if (abs(a[i] - a[i - 1]) > 1) res = 0;
if (a[i] == a[i - 1]) res = min(res, v + min(v, u));
if (abs(a[i] - a[i - 1]) == 1) res = min(res, min(v, u));
}
printf("%d
", res);
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
C. Pekora and Trampoline
链接:C题链接
题目大意:
跳床,有(n)个跳床,床的高度为(s_i),每在上边跳一次那么(s_i)减小(1),每次跨越着跳,当前在(i)那么一步会跳到(i + s_i)去,每次可以从任意一个跳床开始跳,直到跳出所有跳床算一次操作,那么经过多少次操作,所有的(s_i)都变为(1),注意已经变为(1)的床不会再减小。
思路:
贪心的想,假如我们要是所有的床都变为(1),那么我们每次一定得从最左边跳到最右边,所以不如直接贪心加模拟的方法解题。
1.维护一个(p)数组,(p_i)表示已经在第(i)张床上跳了(p_i)次。
2.更新答案(res += max(0, s[i] - p[i] - 1)),即如果(s[i] - 1 > p[i])说明还需要在第i张床上跳(s[i] - p[i] - 1)次,如果(s[i] - 1 leq p[i]),那么说明第(i)张床上(s[i])已经变为(1)。同时如果在第i张床上跳,因为(s[i])每次减一,直到减法为(1)((s[i] = 1)就没必要跳了),那么会影响后边第(i + s[i], ...,i + 2)床。
3. 还有就是,如果第(i)张床上跳的过多,即(s[i])已经变为(1)了,那么第(i)张床上多余的跳跃次数可以转移到下一张床上。
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
//#pragma GCC optimize(2)
//#pragma GCC optimize(3,"Ofast","inline")
using namespace std;
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '
'
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
const int Mod = 10000007;
LL gcd(LL a, LL b) {
return b ? gcd(b, a % b) : a;
}
const int N = 5010;
int s[N];
int p[N];
void solve() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) scanf("%d", &s[i]);
LL res = 0;
memset(p, 0, sizeof p);
for (int i = 1; i <= n; i++) {
int x = max(0, s[i] - p[i] - 1);
res += x;
for (int j = i + 2; j <= min(n, i + s[i]); j++) {
p[j]++;
}
p[i + 1] += max(p[i] - s[i] + 1, 0);
}
printf("%lld
", res);
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
D. Zookeeper and The Infinite Zoo
链接:D题链接
题目大意:
可以在结点(u)到(u+v)连一条边,当前仅当(u \, & \, v = v)的时候,然后给定(q)个询问,每次两个端点(u_i), (v_i)问是否能从(u_i)到(v_i)。
思路:
首先发现两个性质:
- 假如现结点为(v),那么它一定可以转移到(2 * v),很显然。
- 还有就是要满足(u \, & \, v = v),那么起码满足(u leq v),否则直接(pass)掉即可
然后再推到发现,(u)中(1)的个数必须要大于等于(v)中(1)的个数,为什么呢?
我们令(k = v - u).
我们发现,因为(u \, & \, k = k),那么说明,(u)与(k)的二进制数中存在(1)的位数一定在同一位啊,否则不可能按位与得到(k),然后实现(u)转移到(u+k = v),那么(v)相对于(u)来说肯定是(u)其中的某些(1)进位才得到了(u+k = v),那么所以说(v)中(1)得个数肯定是小于等于(u)中(1)得个数的,举两个例子:
(u_(2) = 011), (k_(2) = 001),那么(v_(2) = 100)。(1)的个数减少了。
(u_(2) = 011), (k_(2) = 011),那么(v_(2) = 110)。(1)的个数不变。
那么单纯满足这个条件就对了吗,当然不是。
还要满足,(v)中的每一位(1)要高于(u)中每一位的(1),总不能越转移越低吧。那么满足这两个条件,(u)到(v)就可以进行转移了,那么上边的操作都可以用(lowbit)运算就可以了
代码:
#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <map>
#include <cstring>
//#pragma GCC optimize(2)
//#pragma GCC optimize(3,"Ofast","inline")
using namespace std;
#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define P2LL pair<long long, long long>
#define endl '
'
#define pub push_back
#define pob pop_back
typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;
const int Mod = 10000007;
int lowbit(int x) {
return x & (-x);
}
void solve() {
int u, v;
scanf("%d%d", &u, &v);
if (u > v) {
puts("NO");
return;
}
int uu = u, vv = v;
int cnt1 = 0, cnt2 = 0;
while (uu) { //u中1的个数
uu -= lowbit(uu);
cnt1++;
}
while (vv) { //v中1的个数
vv -= lowbit(vv);
cnt2++;
}
while (u && v) { //必须往高位推才行
if (lowbit(u) > lowbit(v)) {
puts("NO");
return;
}
u -= lowbit(u), v -= lowbit(v);
}
if (cnt1 >= cnt2) puts("YES");
else puts("NO");
}
int main()
{
//ios::sync_with_stdio(false), cin.tie(nullptr), cout.tie(nullptr);
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}